The cognee permission system
SkillSearchGuides your agent in understanding and changing cognee's user, role, and dataset access permissions.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the The cognee permission system skill
About this capability
Use when working with cognee's permission system — understanding or changing how users, roles, and tenants get access to datasets, how ACL grants work, where permissions are enforced in add/cognify/search/delete, and how the grant records surface in the memory-provenance view.
What this skill tells your AI
The instructions your AI receives, as published by topoteretes/cognee in .claude/skills/cognee-permissions/SKILL.md and read by ahel’s review.
The master switch
ENABLE_BACKEND_ACCESS_CONTROL decides whether any of this runs:
true(default): multi-tenant mode. Every API call requires auth, every dataset operation is permission-checked, and each user+dataset pair gets isolated graph/vector/relational databases (tracked in theDatasetDatabasemodel, supported backends: Kuzu, LanceDB, SQLite, Postgres).false: single-user mode. Permission checks short-circuit to allowed, there is no per-dataset isolation, and every user's operations resolve to the same shared databases and datasets. Authentication is a separate knob:REQUIRE_AUTHENTICATION. Unset, it inherits this switch (so turning access control off also turns auth off) — but ifREQUIRE_AUTHENTICATION=trueis set, endpoints still demand a login; authenticated users are identified but not isolated, all pointing at the same data. The reverse misconfiguration (REQUIRE_AUTHENTICATION=falsewith access control on) is ignored: auth is forced on with a warning, because multi-tenant isolation is meaningless without identity (get_authenticated_user.py).
The core model: principals, permissions, ACL grants
Everything reduces to one relation — a grant: principal × permission
× dataset, stored as one ACL row (modules/users/models/ACL.py).
- Principal (
Principal.py) is polymorphic:User,Role, andTenantall inherit from it. Any of the three can hold a grant, which is how role-wide and tenant-wide access work — one ACL row covers every member. - Permission (
Permission.py) is one of exactly four names, defined inpermissions/permission_types.py:read,write,delete,share.shareis the meta-permission: it gates granting/revoking access for others. - Membership is separate from grants:
UserRoleandUserTenantlink users into roles/tenants. A user's effective access is the union of their own grants and the grants of every role/tenant they belong to.
How grants come into existence
- Dataset creation (
modules/data/methods/create_authorized_dataset.py): the creating user is granted all four permissions on the new dataset. If the user has aparent_user_id(sub-users/agent identities), the parent is auto-granted all four as well — parents always see their children's datasets. - Explicit sharing (
permissions/methods/ authorized_give_permission_on_datasets.py): the caller must holdshareon the target datasets, then any principal (user, role, or tenant) can be granted any permission. Revocation mirrors this (authorized_revoke_permission_on_datasets.py). - Capabilities — tenant-scoped grants of actions, not data (landing
via PR #4302, currently in review): a new
principal_capabilitiestable, keyed on(principal, tenant, capability). Where an ACL row grants access to a dataset, a capability grants an action inside a tenant — the first one beingmanage_users. The catalog of capability names is code (CAPABILITY_TYPESinpermission_types.py), not a database table, "because the code is what gives each name meaning"; only the assignment of a capability to a principal is data.tenant_idis stored on every row because a user can belong to multiple tenants: it pins each grant to the user's membership in one specific tenant, so holding a capability in one tenant never carries over to the same user's other tenants. Resolution (get_effective_capabilities(user, tenant)) returns the union of what the tenant grants all of its members, what the user's roles in that tenant grant, and what the user was granted personally — there is no deny in the model, resolution is gated on actual tenant membership, and the tenant owner short-circuits as holding every capability. Grant/revoke endpoints ride the permissions router.
Where permissions are enforced
The single chokepoint for dataset resolution is
get_authorized_existing_datasets(datasets, permission, user) — every
entrypoint resolves names/IDs through it with the permission it needs:
| Operation | Required permission | Enforcement path |
|---|---|---|
add / cognify / remember | write | dataset resolution before the pipeline runs |
search / recall / visualize | read | dataset resolution; retrieval is restricted to documents of readable datasets |
delete / prune of a dataset | delete | datasets.py resolves with "delete" |
| grant/revoke for others | share | authorized_give/revoke_permission_on_datasets |
Two behaviors worth knowing:
- Denied reads return empty results, not 403. A search against a
dataset you cannot read yields
[]— deliberate, to avoid leaking which datasets exist. When debugging "search returns nothing", check grants before checking the graph.
Roles, tenants, and who may manage them
- User management (listing tenant users, assigning/removing roles,
adding/removing users) is allowed for the tenant owner always, and
today for members of roles named in
USER_MANAGEMENT_ALLOWED_ROLE_NAMES(currently{"admin"},permissions/permission_types.py). That name-matching is a known footgun — any customer group that happens to be called "admin" gets user management — and PR #4302 replaces it: the check becomes "does the requester hold themanage_userscapability in this tenant" (owner always passes), with the role-name match kept only as a deprecated fallback so tenants upgrading from the old check don't lose user management until theiradminrole is granted the capability. - Role visibility: members of a role can see the role itself and their
co-members; anyone with user-management permission sees all
(
tenants/methods/get_users_in_role.py). Lookups are tenant-scoped — a role id from another tenant cannot be used to read that tenant's members.
The grant records in memory provenance (the new grant view)
api/v1/visualize/memory_provenance.py surfaces the ACL grants as
first-class graph data. Each grant becomes an AclGrantRecord:
{"principal_id": ..., "principal_kind": "user" | "role" | "tenant", "permission": ...}
and is rendered into the provenance graph as an edge from the principal
node to the dataset, with the permission mapped to a relation name
(_ACL_EDGE_RELATIONS):
| permission | provenance edge |
|---|---|
| read | reads |
| write | writes |
| delete | can_delete |
| share | can_share |
Grants are rendered (never dropped) even when the principal is unknown,
because "an ACL row exists because someone granted it". The view is exposed
through the schema router (get_schema_router.py):
visualize_memory_provenance (HTML) and get_memory_provenance_payload
(JSON) — this is where you see the permission state of a memory rather
than query it.
HTTP API surface (api/v1/permissions/routers/get_permissions_router.py)
| Endpoint | What it does |
|---|---|
POST /permissions/datasets/{principal_id} | grant a permission on datasets to a principal (requires share) |
DELETE /permissions/datasets/{principal_id} | revoke a permission |
POST /permissions/roles · DELETE /permissions/roles/{role_id} | create/delete a role |
POST/DELETE /permissions/users/{user_id}/roles | add/remove a user to/from a role |
POST /permissions/users/{user_id}/tenants | add a user to a tenant |
GET /permissions/tenants/{tenant_id}/roles/{role_id}/users | members of a role (self-visible to members) |
GET /permissions/tenants/{tenant_id}/roles/users/{user_id} | a user's roles |
GET /permissions/tenants/{tenant_id}/users | users in a tenant |
GET /permissions/tenants/me | the caller's tenants |
Key files map
- Models:
cognee/modules/users/models/—ACL,Principal,Permission,Role,Tenant,UserRole,UserTenant,DatasetDatabase(andPrincipalCapabilityonce #4302 lands) - Methods:
cognee/modules/users/permissions/methods/— grant/revoke, checks, dataset resolution, document filtering - Enforcement chokepoint:
cognee/modules/data/methods/(get_authorized_existing_datasets,create_authorized_dataset) - Grant provenance view:
cognee/api/v1/visualize/memory_provenance.py - HTTP API:
cognee/api/v1/permissions/routers/get_permissions_router.py
Signals
- GitHub stars
- 31k
- Forks
- 3k
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
cognee-permissions- Source
- github.com/topoteretes/cognee