Skip to main content

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 to a dataset with a specific permission.
Runtime permission calculation — The system doesn’t store “effective permissions” anywhere—it calculates them on demand by querying ACL entries.

How ACL Works

When a user 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’s direct permissions with those inherited from their tenant and roles, combining all three sources: direct user permissions, tenant-level permissions, and role-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:
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 (user, tenant, or role)
  • dataset_id: References the dataset
  • permission_id: References the permission type
  • created_at: Timestamp when created
  • updated_at: Timestamp when last modified
  • get_all_user_permission_datasets(user, permission): Queries ACL entries and returns datasets the user can access
  • give_permission_on_dataset(principal, dataset_id, permission): Creates or updates ACL entries

Permission Resolution Order

The system evaluates permissions in a specific order:
  1. Direct user permissions — Explicitly granted to the user
  2. Role permissions — Inherited through the user’s role memberships
  3. Tenant permissions — Inherited through the user’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:
Error: Request owner does not have necessary permission: [write] for all datasets requestedThis 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 adding data to or running cognify on a dataset owned by a different user.Check the following:
  • Required permissionadd 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:
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):
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 for complete setup examples.
If you get UnauthorizedDataAccessError: Dataset ... not accessible (401) when deleting data, the most common causes are:delete permission not grantedread and write access are not sufficient for deletion. The delete permission must be granted separately.Verify which datasets a user can delete:
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):
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 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 delete data. If the dataset was created by user1, user2 cannot delete from it without a permission grant from user1.
Review database indexes and query patterns, especially on principal_id, dataset_id, and permission_id, since those fields drive ACL lookups.

Snippets

See practical snippets of ACL operations

Setup Configuration

Learn how to configure ACL and multi-tenant mode