Docs/Access Control
Security

Access Control

AES-256-GCM wallet-derived encryption enforces access cryptographically — no server policy, no override key.

How access works

MNEMOS does not have an access control list or server-side permission model. Access to a memory is equivalent to possessing the AES-256-GCM key — and that key is only derivable by signing with your wallet's private key.

The server stores only ciphertext. It is structurally incapable of decrypting your memories because the key is never transmitted or stored anywhere outside your browser session.

Can read
  • The wallet that signed the encryption key
  • Anyone you share a vault with (shared vault members)
Cannot read
  • MNEMOS servers
  • Database administrators
  • Other wallets
  • Anyone without your private key signature

Key derivation

The encryption key is derived once per session when you sign the fixed domain-separation message. The same wallet always derives the same key — deterministic but non-extractable.

text
Fixed signing message (SIGN_MESSAGE constant):
  "MNEMOS_ENCRYPTION_KEY_v1

   Signing this message derives your personal encryption key.
   It does not create a transaction or cost any gas."

Derivation steps:
  1. signature = wallet.signMessage(SIGN_MESSAGE)
  2. hex → Uint8Array(sigBytes)
  3. keyMaterial = await crypto.subtle.digest("SHA-256", sigBytes)
  4. cryptoKey = await crypto.subtle.importKey(
       "raw", keyMaterial, { name: "AES-GCM", length: 256 },
       false,               // non-extractable
       ["encrypt", "decrypt"]
     )
TIP
The key is non-extractable — once imported into WebCrypto it cannot be read back out of the browser, even by JavaScript in the same tab.

Memory sources and encryption

The source field determines how a memory's access is controlled:

SourceEncryptedAccess model
vaultYes — AES-256-GCM (wallet key)Wallet signature required to decrypt
api-plainNo — plaintext in Neon PostgresBearer API key
mcpNo — temporary session cache onlyActive AI Vault Bridge session (60 min TTL)
WARNING
Never ingest sensitive content via the API ingest endpoint (api-plain source). It is stored as readable plaintext — use it only for non-sensitive external data.

Shared vaults

Shared vaults use ECDH key exchange so multiple wallets can read the same encrypted memories without any member's private key being shared.

text
Shared vault key exchange:
  1. Vault owner generates a random symmetric vault key (AES-GCM, 256-bit)
  2. Owner encrypts vault key for each invitee using ECDH:
       - Owner's vault key is wrapped with AES-GCM(ECDH shared secret)
       - Invitee registers their P-256 ECDH public key
       - Result: wrappedVaultKey + ephemeralPubkey stored per member
  3. On accept, invitee derives ECDH shared secret and unwraps vault key
  4. Shared memories are encrypted with the vault key (not wallet key)
  5. All members can decrypt — none see each other's wallet keys

Revoking MCP access

From the vault UI

Navigate to API Gateway → AI Vault Bridge and click Revoke. Clears the session cache immediately — Claude loses access instantly.

Via API

typescript
const timestamp = Date.now()
const message = `mnemos:revoke-session:${walletAddress}:${timestamp}`
const signature = await wallet.signMessage(message)

await fetch("/api/vault/session", {
  method: "DELETE",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ walletAddress, timestamp, signature }),
})

API key security

API keys grant read/write access to api-plain memories and the MCP session cache. They do not grant access to wallet-encrypted memories.

  • Never commit API keys to source control — use environment variables
  • MNEMOS stores only the SHA-256 hash — the raw key is shown once at generation
  • Rotate immediately if you suspect exposure — go to API Gateway → Generate new key