cognee-permissions skill
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.
Is the cognee-permissions skill safe?
Clean: nothing in its files matched our rules. We read 1 file in the folder on 2026-09-28.
No findings.
Install the cognee-permissions skill
A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.
git clone --depth 1 https://github.com/topoteretes/cognee.git /tmp/cognee mkdir -p ~/.claude/skills cp -r /tmp/cognee/.claude/skills/cognee-permissions ~/.claude/skills/cognee-permissions
In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub
The instructions your agent would load
SKILL.md as published, without the frontmatter. Read it on GitHub
The cognee permission system
The master switch
ENABLEBACKENDACCESS_CONTROL decides whether any of this runs:
dataset operation is permission-checked, and each user+dataset pair gets isolated graph/vector/relational databases (tracked in the DatasetDatabase model, supported backends: Kuzu, LanceDB, SQLite, Postgres).
- true (default): multi-tenant mode. Every API call requires auth, every
there is no per-dataset isolation, and every user's operations resolve to the same shared databases and datasets. Authentication is a separate knob: REQUIREAUTHENTICATION. Unset, it inherits this switch (so turning access control off also turns auth off) — but if REQUIREAUTHENTICATION=true is set, endpoints still demand a login; authenticated users are identified but not isolated, all pointing at the same data. The reverse misconfiguration (REQUIREAUTHENTICATION=false with access control on) is ignored: auth is forced on with a warning, because multi-tenant isolation is meaningless without identity (getauthenticated_user.py).
- false: single-user mode. Permission checks short-circuit to allowed,
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).
Tenant all 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.
- Principal (Principal.py) is polymorphic: User, Role, and
permissions/permission_types.py: read, write, delete, share. share is the meta-permission: it gates granting/revoking access for others.
- Permission (Permission.py) is one of exactly four names, defined in
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.
- Membership is separate from grants: UserRole and UserTenant link
How grants come into existence
the creating user is granted all four permissions on the new dataset. If the user has a parentuserid (sub-users/agent identities), the parent is auto-granted all four as well — parents always see their children's datasets.
- Dataset creation (modules/data/methods/createauthorizeddataset.py):
authorizedgivepermissionondatasets.py): the caller must hold share on the target datasets, then any principal (user, role, or tenant) can be granted any permission. Revocation mirrors this (authorizedrevokepermissionondatasets.py).
- Explicit sharing (permissions/methods/
via PR #4302, currently in review): a new principalcapabilities table, 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 being manageusers. The catalog of capability names is code (CAPABILITYTYPES in permissiontypes.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. tenantid is 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 (geteffective_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.
- Capabilities — tenant-scoped grants of actions, not data (landing
Where permissions are enforced
The single chokepoint for dataset resolution is getauthorizedexisting_datasets(datasets, permission, user) — every entrypoint resolves names/IDs through it with the permission it needs:
Two behaviors worth knowing:
dataset you cannot read yields [] — deliberate, to avoid leaking which datasets exist. When debugging "search returns nothing", check grants before checking the graph.
- Denied reads return empty results, not 403. A search against a
Roles, tenants, and who may manage them
adding/removing users) is allowed for the tenant owner always, and today for members of roles named in USERMANAGEMENTALLOWEDROLENAMES (currently {"admin"}, permissions/permissiontypes.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 the manageusers capability 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 their admin role is granted the capability.
- User management (listing tenant users, assigning/removing roles,
co-members; anyone with user-management permission sees all (tenants/methods/getusersin_role.py). Lookups are tenant-scoped — a role id from another tenant cannot be used to read that tenant's members.
- Role visibility: members of a role can see the role itself and their
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 (ACLEDGE_RELATIONS):
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 (getschemarouter.py): visualizememoryprovenance (HTML) and getmemoryprovenancepayload (JSON) — this is where you see* the permission state of a memory rather than query it.
HTTP API surface (api/v1/permissions/routers/getpermissionsrouter.py)
Key files map
Role, Tenant, UserRole, UserTenant, DatasetDatabase (and PrincipalCapability once #4302 lands)
- Models: cognee/modules/users/models/ — ACL, Principal, Permission,
checks, dataset resolution, document filtering
- Methods: cognee/modules/users/permissions/methods/ — grant/revoke,
(getauthorizedexistingdatasets, createauthorized_dataset)
- Enforcement chokepoint: cognee/modules/data/methods/
- Grant provenance view: cognee/api/v1/visualize/memory_provenance.py
- HTTP API: cognee/api/v1/permissions/routers/getpermissionsrouter.py
More skills from topoteretes/cognee
- Acognee-cliUse when the user wants to drive cognee from the terminal with cognee-cli — remember/recall/forget/improve memory commands, managing datasets and config, or database migrations.
- Acognee-communityUse when the user needs something that ships outside cognee core — community database adapters (Qdrant, Milvus, Weaviate, Redis, Pinecone, FalkorDB, Memgraph, DuckDB, NetworkX, …), data-source connectors (Slack, Gmail, Notion, Confluence, Google Drive), custom tasks/pipelines/retrievers (Exa, ScrapeGraph, codify), Keywords AI observability — or wants to contribute a package to the cognee-community repo.
- Acognee-dockerUse when the user wants to run cognee with Docker or docker compose — trying it out from the prebuilt image, starting the API server in a container, or bringing up the full stack (UI, MCP, Postgres, Neo4j) with compose profiles.
- Acognee-installUse when the user wants to install cognee and run their first remember → recall flow with the Python SDK — fresh setup, virtual env, extras selection, or a minimal working example.
- Acognee-integrationsUse when the user wants to connect cognee to external services — switching LLM or embedding providers (OpenAI, Azure, Gemini, Anthropic, Ollama, OpenRouter), changing databases (Postgres, PGVector, Neo4j, Neptune, Turso), S3 storage, or the MCP server for IDE integration.
- Acognee-serverUse when the user wants to run the cognee API server (and optional UI) on their own machine — starting it, checking it's healthy, connecting the SDK or other clients to it, and choosing the right auth posture.
- Adiff-risk-explainerUse to briefly explain small code diffs.
- Apr-comment-evaluatorUse to judge whether a PR review comment sounds polite.
- Askill-feedback-writerUse to identify missing instructions in another skill based on its output.