Security and isolation of the memory
Authentication always on
| Engine | Mechanism | Verified safeguards |
|---|---|---|
| Mem0 | AUTH_DISABLED=false; admin key X-API-Key, user API keys (m0sk_...) or JWT | call without a key or with a wrong key: 401; public registration closed (403), the first administrator being created by the launcher before the API listens; a revoked key is refused |
| Cognee | REQUIRE_AUTHENTICATION and ENABLE_BACKEND_ACCESS_CONTROL set to true; key X-Api-Key stored as a salted hash | call without a key: 401; the upstream account default_user@example.com does not exist; public registration refused (403) except with the service key; cloud sync refused (403) |
These safeguards are proven by test_mem0_real.py and test_cognee_real.py. In Cognee the URL ingestion
(allow_url_ingestion) and the Cypher queries (allow_cypher) are off by default, and a local file outside the data
root is refused (415). The MCP server of Cognee has no authentication of its own: it listens on 127.0.0.1 only.
Telemetry off and no outbound traffic
Mem0 runs with MEM0_TELEMETRY=false (the launcher refuses any other value). Cognee runs with TELEMETRY_DISABLED,
RUNTIME__DLTHUB_TELEMETRY=false, HF_HUB_OFFLINE, HF_HUB_DISABLE_TELEMETRY, TRANSFORMERS_OFFLINE and
DO_NOT_TRACK. The real tests send every outbound HTTP(S) of the engines to a recording proxy that must stay unused:
it stayed unused.
Secrets
- Generated and sealed by the Manager at installation: for Mem0
admin_api_key,jwt_secret,admin_password; for Cogneeadmin_password,service_api_key,jwt_secret,reset_password_secret,verification_secret,api_key_salt. - Never on the command line nor in the logs: the tests check that no value appears in the arguments nor in the API journal, and the functional test masks the key.
- Rotation: a new value and a restart for the API keys; changing a JWT secret ends every session; changing
api_key_saltinvalidates every Cognee API key except the service one. - In Navin Enterprise the keys of the platform instances come from the sensitive environment file
(
env://EAI_CRED_MEMORY_MEM0_API_KEY,env://EAI_CRED_MEMORY_COGNEE_API_KEY); the key of a tenant on a shared instance is an OpenBao reference (openbao://...). They are resolved server side, never returned nor logged.
Derived context and scope mapping
The context of an operation (tenant, organization, project, user, agent, session, scope, team) is derived from the authenticated identity and the agent registry, never from parameters sent by an agent. A controlled mapping turns it into engine identifiers:
| Engine | Identifiers used |
|---|---|
| Mem0 | user_id, agent_id, run_id and the metadata (navin_ns and the scope fields) |
| Cognee | the dataset name |
Each identifier carries an opaque prefix, an HMAC of the instance, the tenant and the project, that cannot be guessed.
Isolation guarantee and refusal
| Guarantee | When |
|---|---|
logical | identifiers and prefix enforced by Navin only: any shared Mem0 instance (its keys do not scope the memories), a shared Cognee instance without a user of the tenant |
backend | separation enforced by the engine: builtin (PostgreSQL row level security), Cognee with access control and a Cognee user of the tenant |
dedicated_instance | instance installed by the Manager for this tenant only and declared with its tenant_id |
The agent policy sets required_isolation. When the chosen instance guarantees less, the operation is refused with
memory_isolation_unsatisfied (HTTP 409): "a dedicated instance installed by the Navin Manager is required". For a
bank, require dedicated_instance and install one Mem0 or Cognee instance per tenant. The separation of two users in
one Cognee instance is proven by test_cognee_real.py: one neither sees, reads nor searches the datasets of the
other until a read permission is given.
Authorization, policies and audit
- OpenFGA: the caller must be the agent itself or hold a relation on it (
can_invokefor memories and knowledge,vieweroreditorfor the policy). - OPA: each write goes through the decision
memory.write, each read throughmemory.search, before the engine call. - Roles: declaring or removing an instance needs
platform_admin; setting the key of a tenanttenant_admin. - Audit: each operation writes an event (operation, context, engine, decision, result), never the content of the
memories. A refusal or an engine failure also writes a
denyorfailureevent.
Recalled memories and secrets
- Recalled memories are untrusted data, never instructions. Their content goes through the prompt injection screen
of the gateway (a blocked item is dropped), then each item is wrapped in the untrusted data markers and given to the
Context Manager as a
memorysegment. Permissions are checked before anything is given to the LLM. - Secrets, passwords, tokens and keys are never memorized: the DLP detects them and the write is refused with
memory_secret_refused(HTTP 422). Personal data is refused when the policy setsallow_personal_datatofalse.