Compartment Security Model
July 28, 2026 · View on GitHub
Honest threat model. Read the "cannot protect against" section too. I can honestly say Compartment has the best security of any vector memory. Important, as vector memory literally holds the entire computer's memory and essentially all chat logs + everything else from all operations. Vector memory is the #1 target for hackers, because of its high value and (until now) total lack of any security.
Cryptography
| Purpose | Primitive |
|---|---|
| All encryption at rest | XChaCha20-Poly1305 AEAD (libsodium via PyNaCl) |
| Key derivation | Argon2id (t=3, m=64MiB, p=4; params stored in header) |
| Keyslots | LUKS-style: random 256-bit master key wrapped per slot |
| Recovery | none. Your passphrase is the only credential; nothing is generated for you |
| Per-record keys | wrapped by master key → crypto-shred on forget --shred |
| Signing (vaults + packs) | Ed25519 |
| Integrity | AEAD tags everywhere + hash-chained audit log |
Vaults created before 1.8.0 were issued a 16-word recovery phrase in their own keyslot. Those still open with that phrase, but no new vault is given one.
No homemade crypto. AAD binds every ciphertext to its role and vault (payloads, journal entries by sequence number, keyslots, record bodies by record id) - ciphertexts cannot be transplanted between contexts.
What Compartment protects against
- Stolen disk / stolen laptop (vault at rest): the vault file is a single AEAD-sealed blob under an Argon2id-wrapped key. No plaintext, no plaintext index, no temp files, no logs exist on disk - ever (I2).
- Vault interception in transit: a locked vault is safe to move over
any channel. Optional Ed25519 manifest (
lock --sign) lets the recipient verify origin and integrity without any credential. - Tampering: any modified bit fails AEAD authentication loudly. Truncating the file, editing the header, splicing journal entries, and downgrade-style version games all produce specific errors.
- Malicious or modified memory packs: the Ed25519 signature is checked
against a TRUSTED key, never against the
signer_pubthe pack carries. A pack cannot vouch for itself: anyone can mint a keypair and sign anything, so a self-consistent signature proves nothing about authorship. Only the project key ships as trusted; a third party key has to be named deliberately (compartment pack install --trusted-key HEX). Signature, then content hash, then decrypt, then parse. Failure aborts install. - Forensic recovery of deleted memories:
forget --shreddestroys the per-record key, deletes the row + FTS entries, VACUUMs, and rewrites the payload - the content is gone from the current vault file and its ciphertext history within that file. - Cross-agent memory access: per-caller namespace grants (rw/ro/none);
packs/*immutable for everyone. Run one server instance per host for boundary enforcement (see limitations). - Embedding inversion: vectors can be partially inverted back toward text, so Compartment encrypts vectors like everything else. No plaintext vector index ever exists on disk.
- Stored prompt injection: memories recalled from storage are wrapped
with a data-not-instructions notice; content stored from untrusted
sources can be flagged
quarantined, which attaches an explicit warning envelope to every future recall. This is a mitigation, not a guarantee - the host agent must still treat memory as data. - History falsification: the audit log is hash-chained;
compartment audit verifyreports the first broken link.
What Compartment CANNOT protect against
- A compromised OS while the vault is unlocked. Anything that can read this process's RAM can read the working set and the master key. This is true of every encryption product; unlock only on machines you trust.
- RAM at unlock time generally - including swap in pathological cases.
Python cannot guarantee zeroization (the GC may copy buffers);
lockwipes what it can, best-effort. A future Rust core would tighten this. - Pre-shred backups. Crypto-shred removes content from the current vault. Copies made before the shred still contain it (encrypted).
- A hostile host agent within its granted namespaces. The
--calleridentity is declarative. A host that lies about its name gets that name's grants. For real isolation, run onecompartment serveper host with its own config file and OS-level separation. - Weak passphrases. Argon2id slows attackers; it cannot save
"password1". The passphrase is user-chosen and is the ONLY credential -
Compartment never generates a password or recovery seed, so there is nothing
written down anywhere unless you write it. Losing the passphrase (and
the 2FA keyfile, if enrolled) makes the vault permanently
unrecoverable; that is the design, not a failure mode. To harden a
human-memorable passphrase, enable two-factor unlock:
compartment 2fa enablewraps the master key under Argon2id(passphrase ‖ keyfile), so both the thing you know and the file you hold are required - a cryptographic requirement, not a prompt that malware can skip. (Why not authenticator-app codes? A six-digit TOTP cannot cryptographically protect an offline file: the verifying secret would have to live next to the data it guards, so any code-based gate on a local vault is theater. The keyfile is the honest second factor for this threat model.) - Search-audit persistence gap: search audit entries are held in RAM until the next save/lock (writes are journaled immediately; reads don't cost an fsync). A kill -9 can lose recent search audit entries - never store/forget entries.
Unlock paths, ranked
- Boot-session credential (the default).
compartment unlockwraps the master key under HMAC-SHA256 keyed by a random 32-byte per-boot secret, together with the boot timestamp, uid and machine id, and stores it 0600 in~/.compartment/session/. The secret is held in a volatile kernel object - a POSIX shared-memory segment on macOS and Linux, a volatile registry key on Windows - and is never written to any filesystem, so a backup, snapshot or disk image does not contain it. A restart destroys it, which is what makes the relock real rather than a policy. On Windows a full logoff relocks as well. A stolen file on its own is therefore useless: guessing the boot time, uid and machine id still leaves 32 unknown bytes. It remains a convenience credential - anything running as you on the live machine reads the same secret, and could read the master key out of process RAM anyway, and a RAM capture (hibernation image, suspended VM, swap or pagefile) can expose it too. It is not a second factor. Where no volatile holder exists, storing a credential is REFUSED rather than falling back to values an attacker would already hold. - macOS Keychain (
compartment unlock --keychain, explicit opt-in): credential guarded by the OS keychain. Stronger against file theft than the session credential, but it SURVIVES REBOOTS - choose it only if that is what you want. COMPARTMENT_PASSPHRASEenv var: for scripts/CI; visible to anything that can read the process environment.memory_unlockMCP tool: DISABLED by default. The passphrase would transit the agent's context window and possibly the host's logs/model provider. Enable only if you accept that (settings.unlock_tool_enabledin<vault>.config.json).
compartment lock and the memory_lock panic tool clear ALL stored
credentials (session + keychain). Auto-lock drops the in-RAM key after
auto_lock_minutes (default 30) idle; while a stored credential remains,
the next operation silently re-opens - stored credentials represent
standing user intent, ended by compartment lock or a reboot.
Two-factor interaction, stated honestly: with compartment 2fa enable,
every credential-based unlock (passphrase paths 3-4 above, and any
fresh compartment unlock) requires the keyfile too - that is enforced by
the KDF. The session and keychain paths store the unwrapped MASTER key,
so after one successful two-factor unlock they re-open the vault without
re-presenting the keyfile, exactly as they skip the passphrase. That is
the design (they encode standing intent on an already-trusted machine);
if you want every single open to need both factors, run compartment unlock --once and don't store a credential.
Multi-agent, one vault
Several agent processes (Hermes provider, Claude via MCP, the CLI) may
share one vault: an advisory file lock serializes every journal append and
save, and each process detects foreign writes (mtime/size) and reloads
before proceeding - a stale writer gets a loud VaultStaleError, never
silent corruption. Namespace ACLs are per-caller; --caller identity is
declarative (see the hostile-host limitation above).
Reporting
Report vulnerabilities privately to the maintainer. No telemetry exists in this product; nothing phones home, so nothing can be recalled remotely.