> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognee.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# ACL

> Access Control List system for permission storage and inheritance in Cognee

# ACL: Permission Storage and Inheritance

The ACL (Access Control List) system stores all permissions and handles permission checking at runtime. ACL entries are stored in the `acls` table, with each row linking a [principal](./principals) to a [dataset](./datasets) with a specific permission.

<Info>**Runtime permission calculation** — The system doesn't store "effective permissions" anywhere—it calculates them on demand by querying ACL entries.</Info>

## How ACL Works

When a [user](./users) tries to access data, the system queries all relevant ACL entries and aggregates the permissions. The permission checking function `get_all_user_permission_datasets()` unions the [user](./users)'s direct permissions with those inherited from their [tenant](./tenants) and [roles](./roles), combining all three sources: direct [user](./users) permissions, [tenant](./tenants)-level permissions, and [role](./roles)-level permissions.

This approach ensures permissions are always current and allows for complex permission inheritance without data duplication.

## ACL Storage Schema

The ACL system uses a simple but powerful schema to store permissions:

<Accordion title="ACL Model Fields">
  The ACL model defines what gets stored in the SQL database. The `acls` table contains:

  * `id`: Unique identifier (UUID primary key)
  * `principal_id`: References the [principal](./principals) ([user](./users), [tenant](./tenants), or [role](./roles))
  * `dataset_id`: References the [dataset](./datasets)
  * `permission_id`: References the permission type
  * `created_at`: Timestamp when created
  * `updated_at`: Timestamp when last modified
</Accordion>

<Accordion title="Permission Checking Functions">
  * `get_all_user_permission_datasets(user, permission)`: Queries ACL entries and returns [datasets](./datasets) the [user](./users) can access
  * `give_permission_on_dataset(principal, dataset_id, permission)`: Creates or updates ACL entries
</Accordion>

## Permission Resolution Order

The system evaluates permissions in a specific order:

1. **Direct [user](./users) permissions** — Explicitly granted to the [user](./users)
2. **[Role](./roles) permissions** — Inherited through the [user](./users)'s role memberships
3. **[Tenant](./tenants) permissions** — Inherited through the [user](./users)'s tenant membership

This order allows for flexible permission management where more specific permissions can override broader ones.

## ACL Operations

The ACL system supports several key operations:

* **Grant permissions** — Add new ACL entries to grant access
* **Revoke permissions** — Remove ACL entries to revoke access
* **Check permissions** — Query ACL entries to determine access
* **List permissions** — Get all permissions for a principal or dataset

## Permission Inheritance

The ACL system implements a three-tier inheritance model:

* **User level** — Direct permissions granted to individual users
* **Role level** — Permissions granted to roles, inherited by role members
* **Tenant level** — Permissions granted to tenants, inherited by all tenant members

Users receive the union of all permissions from these three sources, giving them the most permissive access available.

## Performance Considerations

The ACL system is designed for performance:

* **Indexed queries** — Database indexes on principal\_id, dataset\_id, and permission\_id
* **Efficient lookups** — Single query to get all permissions for a user
* **Caching opportunities** — Permission results can be cached for frequently accessed datasets
* **Batch operations** — Support for granting/revoking multiple permissions at once

## Security Features

The ACL system includes several security features:

* **Immutable ownership** — Dataset ownership cannot be changed
* **Permission validation** — All permission checks go through the ACL system
* **Audit trail** — All permission changes are logged with timestamps
* **Isolation** — Users can only access datasets they have permissions for

## Troubleshooting

Common ACL-related issues and solutions:

<AccordionGroup>
  <Accordion title="PermissionDeniedError on memory writes (Error code #403)">
    **Error:** `Request owner does not have necessary permission: [write] for all datasets requested`

    This error occurs when `ENABLE_BACKEND_ACCESS_CONTROL=true` and the calling user does not have `write` permission on the target dataset. This commonly happens when running `remember()`, `improve()`, or the lower-level `add()` / `cognify()` flow on a dataset owned by a different user.

    Check the following:

    * **Required permission** — memory-writing operations such as `remember()`, `improve()`, `add()`, and `cognify()` require `write` permission on every target dataset. Having `read` access is not enough.
    * **ACL entries** — Verify that ACL rows actually exist for the expected principal and dataset. If no matching ACL entries exist, the user will not inherit access.
    * **Role or tenant inheritance** — If access should come from a role or tenant, confirm that the user is a member of that role or tenant. Effective permissions are the union of direct user grants plus inherited tenant and role grants.

    If the user is missing `write`, the dataset owner (or any user with `share` permission on the dataset) must grant it.

    **Python SDK:**

    ```python theme={null}
    from cognee.modules.users.permissions.methods import authorized_give_permission_on_datasets

    # Called by the dataset owner (or a user with 'share' permission)
    await authorized_give_permission_on_datasets(
        user.id,        # user who needs write access
        [dataset.id],   # target dataset UUIDs
        "write",        # permission type
        owner.id,       # must have 'share' permission on the dataset
    )
    ```

    **HTTP API** (requires authentication as the dataset owner):

    ```bash theme={null}
    curl -X POST "http://localhost:8000/v1/permissions/datasets/{user_id}" \
      -H "Authorization: Bearer <owner_token>" \
      -H "Content-Type: application/json" \
      -d '{"permission_name": "write", "dataset_ids": ["<dataset_uuid>"]}'
    ```

    To retrieve the user's `id`, call `GET /v1/users/me`. To list available datasets and their IDs, call `GET /v1/datasets`.

    See [Permission Snippets](/guides/permission-snippets) for complete setup examples.
  </Accordion>

  <Accordion title="Dataset not accessible during forget or delete (Error code #401)">
    If you get `UnauthorizedDataAccessError: Dataset ... not accessible (401)` when forgetting or deleting data, the most common causes are:

    **`delete` permission not granted** — `read` and `write` access are not sufficient for deletion. The `delete` permission must be granted separately.

    Verify which datasets a user can delete:

    ```python theme={null}
    from cognee.modules.users.permissions.methods import get_all_user_permission_datasets

    deletable_datasets = await get_all_user_permission_datasets(user, "delete")
    ```

    Grant `delete` permission (requires the granting user to have `share` permission on the dataset):

    ```python theme={null}
    from cognee.modules.users.permissions.methods import authorized_give_permission_on_datasets

    await authorized_give_permission_on_datasets(
        user.id,        # user to receive permission
        [dataset.id],   # target datasets
        "delete",       # permission type
        owner.id,       # must have 'share' permission on the dataset
    )
    ```

    **Wrong user context** — When `ENABLE_BACKEND_ACCESS_CONTROL` is enabled, every operation runs as a specific user. Ensure the `user` object passed to `forget()` or lower-level delete functions is the user that owns or has `delete` access to the dataset.

    **Dataset owned by another user** — Only the dataset owner and users with an explicit `delete` grant can remove data. If the dataset was created by `user1`, `user2` cannot delete from it without a permission grant from `user1`.
  </Accordion>

  <Accordion title="Slow permission checks">
    Review database indexes and query patterns, especially on `principal_id`, `dataset_id`, and `permission_id`, since those fields drive ACL lookups.
  </Accordion>
</AccordionGroup>

<Columns cols={2}>
  <Card title="Snippets" icon="code" href="/guides/permission-snippets">
    See practical snippets of ACL operations
  </Card>

  <Card title="Setup Configuration" icon="settings" href="/setup-configuration/permissions">
    Learn how to configure ACL and multi-tenant mode
  </Card>
</Columns>
