Security Controls
Authentication
Authentication
Require authentication for all API requests
ENABLE_BACKEND_ACCESS_CONTROL is the canonical posture switch; REQUIRE_AUTHENTICATION is an optional override on the auth requirement alone.When neither variable is set, the default is multi-tenant mode with authentication required.
At startup, Cognee logs an
auth posture: ... line summarizing the resolved decision and the reason (default, inherited from ENABLE_BACKEND_ACCESS_CONTROL, explicit REQUIRE_AUTHENTICATION, or forced on by multi-tenant mode). Use this log line to verify what is actually in effect after deployment.JWT token settings
FASTAPI_USERS_JWT_SECRET must be the same across all instances (e.g., all Kubernetes pods) so that a token issued by one pod is accepted by another. Use a long, randomly generated string in production and never commit the real value to version control.JWT_LIFETIME_SECONDS controls how long a bearer token or cookie remains valid before the user must log in again.Default user credentials
When an operation runs without an explicit authenticated user, Cognee falls back to a built-in default user — created on first use as a superuser. This happens for SDK/library calls, and for HTTP requests when authentication is off (single-user mode). Override its credentials with:ENABLE_BACKEND_ACCESS_CONTROL=true, HTTP endpoints require an authenticated user, so the default user is not used to serve unauthenticated requests — but SDK calls still fall back to it. Set DEFAULT_USER_EMAIL and DEFAULT_USER_PASSWORD before the default user is first created to give this auto-created superuser known credentials you can then log in with. Changing these values later does not update an existing user; update or recreate that user separately, then restart the process so Cognee reads the new environment. See Users for the full permission model.Data Protection
Data Protection
API Key Storage
false, API keys are stored as plaintext in the relational database.
When true, each key is hashed with SHA-256 before storage. The raw key is shown to the user only once at creation time and cannot be recovered afterward.Local File System Access
true, Cognee accepts local filesystem paths as data sources (e.g., /etc/passwd). This is convenient for local development but dangerous when Cognee is exposed as a multi-user backend — an authenticated user could read arbitrary files that the Cognee process has access to.Set to false when running Cognee as a backend service:file:// URIs and path strings that resolve to an existing local file; those inputs raise IngestionError: Local files are not accepted. when it is disabled. It is not a filter on submitted text: an absolute-looking string that does not point to an existing file is ingested as text content either way, since Cognee never treats it as a path.Cypher Query Access
true, users can execute raw Cypher queries against the graph database (SearchType.CYPHER) and use natural language-to-Cypher translation (SearchType.NATURAL_LANGUAGE). Disable this to limit users to higher-level semantic search only:Outbound HTTP Requests (SSRF Protection)
http(s) URL (via add() → save_data_item_to_storage) or crawls a page, it fetches that URL server-side. To prevent Server-Side Request Forgery (SSRF, CWE-918), every user-supplied outbound URL is now validated before any request is made.ALLOW_HTTP_REQUESTS (default true) gates outbound HTTP(S) fetching. Set it to false to disable all remote URL ingestion — any http(s) URL is then rejected:false, 0, no, and off (case-insensitive); any other value leaves outbound fetching enabled.When outbound requests are allowed, each URL still passes through these checks before it is fetched:- Scheme — only
httpandhttpsare permitted. Other schemes (e.g.ftp://,gopher://) are rejected. - Host — the URL must contain a host, and that host must resolve. Hostless or unresolvable URLs are rejected.
- Resolved address — the host is resolved and every resolved IP is checked. The request is blocked if any address is loopback, private, link-local, reserved, multicast, unspecified, or IPv6 site-local. This blocks internal and cloud-metadata targets such as
127.0.0.1,::1,169.254.169.254,10.0.0.0/8,172.16.0.0/12, and192.168.0.0/16. IP-literal URLs (e.g.http://[::1]) are validated directly, so DNS-rebinding and IP-literal bypasses are also caught.
follow_redirects=False — redirects are not followed automatically.Encrypting Neo4j Credentials
When using theneo4j_aura_dev or neo4j_community dataset database handler for multi-user mode, Cognee stores per-dataset Neo4j connection info in the relational database — for neo4j_aura_dev the provisioned Aura instance, for neo4j_community the per-dataset Docker container. The stored database password is encrypted with Fernet symmetric encryption; both handlers derive the encryption key from the same NEO4J_ENCRYPTION_KEY:"test_key" is intentionally insecure. Replace it with a long random string in any environment that stores real Neo4j credentials.The Aura API credentials used to create or delete instances (
NEO4J_CLIENT_ID, NEO4J_CLIENT_SECRET, and NEO4J_TENANT_ID) are read from environment variables when needed and are not stored in the relational database by this handler.Encrypting Integration Credentials
Third-party OAuth integrations (Slack, GitHub, and Linear) store their tokens in the relationalintegration_credentials table. The token payload is encrypted with AES-256-GCM under a fresh random 96-bit nonce per write; only non-secret metadata — the workspace label, Slack team and enterprise ids, the bot and installing member’s user ids, the granted OAuth scopes, and the allowed-channel list — is stored in the clear.Keys are configured as a keyring — a set of 32-byte keys, each addressed by a short key id — so that keys can be rotated without re-encrypting existing rows:There is no default key and no derived fallback: if neither variable is set, or a key does not decode to exactly 32 bytes, credential writes fail with a
RuntimeError rather than producing ciphertext nobody can decrypt later. Generate a key with:INTEGRATION_CREDENTIALS_KEYS alongside the old one under a new id, then point INTEGRATION_CREDENTIALS_ACTIVE_KEY_ID at it. Every row records the key id it was written with, so existing rows keep decrypting under their original key; reconnecting an integration rewrites its row under the active key. Remove an old key from the ring only once no row still references it.Revocation. Disconnecting an integration marks the stored credential revoked and makes a best-effort call to the provider’s revoke endpoint. Provider-side removal has the same effect: for Slack, an app_uninstalled or tokens_revoked event deactivates the stored installation, so no usable token survives an uninstall.Multi-User Isolation
Multi-User Isolation
Dataset & Multi-User Isolation
If you configure an unsupported backend (e.g., Qdrant, Weaviate), disable access control to avoid runtime errors:
ENABLE_BACKEND_ACCESS_CONTROL=false alone also disables the auth requirement (single-user mode). You only need to add REQUIRE_AUTHENTICATION if you want to override that default — for example, REQUIRE_AUTHENTICATION=true to keep auth on for a single-user deployment behind a shared token.See Dataset Database Handlers for the full list of supported handlers.Disabling Authentication for Local Development
Disabling Authentication for Local Development
When running Cognee locally for development or testing, you can disable authentication so that API calls succeed without a bearer token. Setting the single posture switch is enough:With
ENABLE_BACKEND_ACCESS_CONTROL=false and REQUIRE_AUTHENTICATION unset, REQUIRE_AUTHENTICATION inherits from the posture switch and the auth requirement is turned off automatically. (Previously, both variables had to be set to false independently; that is no longer required.)These values are read once when the server process starts, so you must restart the server after changing them. Check the auth posture: ... line in the startup logs to confirm the resolved decision.When authentication is disabled, unauthenticated requests are automatically served under a built-in default user (default_user@example.com). The relational database must be initialized before the first request so that this default user can be looked up or created.Troubleshooting 401 errors after setting the variablesRecommended Production Settings
Recommended Production Settings
Recommended Production Settings
For detailed instructions on the multi-user permission system (users, tenants, roles, and ACL), see Cognee Permissions System.
Permissions Setup
Enable dataset isolation and access control
Multi-User Mode
Understand multi-tenant architecture