SSD Nodes Learn 🎉 VPS from $4.99/mo
Guides Matt ConnorBy Matt Connor

Compartment: offline encrypted agent memory

Compartment keeps agent memory encrypted on your own machine, fully offline. What that threat model really covers, and what happens if you lose the key.

What Compartment does differently

Compartment is an agent memory store that keeps every record encrypted on the machine that created it and never talks to a network service. Two choices separate it from the rest of the agent memory field. The vault is one sealed file that only your passphrase opens, and the embedding step runs locally, so the text of a memory is never sent anywhere to be turned into a vector. Version 4.6.0 was published on 10 August 2026, the licence is Apache-2.0, and it installs from PyPI.

That is a threat model claim, so this guide treats it as one. Encryption at rest plus an offline design protect a specific list of things. They leave other things open, and that gap is where the day two problems live.

This guide follows the project's own documentation and release notes, read on 11 August 2026. Compartment ships a desktop application, a menu bar item on macOS and a notification area icon on Windows, next to its command line tool, and a passphrase prompt cannot be driven from an automated container. Treat what follows as documented behaviour, not as behaviour measured here. Run it on a spare machine before you trust it with anything real.

What encryption at rest actually protects

The vault is sealed with XChaCha20-Poly1305, an AEAD (authenticated encryption with associated data) cipher, and the keyslots holding the master key are wrapped with Argon2id, a password hashing function built to be slow and to need a lot of memory. Two things follow. A copy of the file on a stolen disk, in an old backup, or attached to a support ticket is bytes and nothing more. And a single flipped bit fails authentication when the file opens, so corruption becomes a loud error instead of a wrong answer.

The embedding vectors are encrypted too, which matters more than it sounds. An embedding is not a hash. Research on embedding inversion has recovered readable fragments of the original text from its vector alone, so a plaintext vector index sitting beside an encrypted database is close to leaving the database open. Compartment writes no plaintext index to disk.

Deletion is real deletion. Each record carries its own key, and compartment forget --shred destroys that key, so the ciphertext left behind cannot be decrypted by anyone, including you. Compare that with a row deleted from a database file, which usually stays readable in free pages until something overwrites it.

Offline is the other half. Nothing is uploaded, so there is no vendor account holding your memories and no API key that can leak them.

What Compartment does not protect

The claim stops at the vault boundary, and that boundary is closer than it looks.

The agent reads plaintext. Recall decrypts a memory and hands the text to the agent. If that agent is a hosted model, the memory travels to the model provider inside the next prompt, exactly like everything else in the context window. Encryption at rest protects the file. It does not protect the retrieval. So the rules in keeping secrets out of AI agents do not relax because the store is encrypted: a password saved as a memory is a password you have arranged to paste into a prompt automatically.

An unlocked vault on a running machine is open. The project's security notes say this plainly. While the vault is unlocked, the master key and the working set sit in RAM, Python cannot guarantee that a buffer is wiped, and swap or a hibernation image can write that memory to disk. Malware running as your user never has to break the cipher, because it can ask the already unlocked vault.

Caller identity is declarative. Namespaces can be restricted per caller, but the caller name arrives from the host process, so a host that lies about its name receives the grants it claims. Namespace permissions are organisation, not a security boundary against a hostile local program.

Shredding cannot reach copies. forget --shred destroys the key inside the current file. A backup taken before the shred still holds that record, and still opens with the passphrase of that day.

A weak passphrase ends the discussion. Argon2id makes each guess expensive. It does not save a passphrase that appears in a word list.

Install Compartment from a pinned release

Compartment needs Python 3.11 or newer. The project moves fast, with thirty versions on PyPI by 10 August 2026, so pin the version instead of taking whatever is current on the day you install.

python3 --version
pip install "compartment==4.6.0"
compartment --version
compartment init

compartment --version should print the version you pinned. If the shell answers compartment: command not found, the install directory is not on your PATH, and on most systems that directory is ~/.local/bin. pipx install compartment==4.6.0 and uv tool install compartment==4.6.0 avoid that by managing their own paths.

compartment init asks for a passphrase twice and echoes nothing. That passphrase is the only key. The project generates no password and no recovery phrase, which is deliberate: there is no credential the software holds that you do not.

Check the result before wiring anything to it.

compartment status

A healthy vault reports that it is unlocked. If it reports locked, run compartment unlock and enter the passphrase. A restart locks it again, because the credential that keeps it open depends on a per boot secret. On macOS, compartment unlock --keychain opts into surviving a reboot by storing that credential in the system keychain.

Where the data actually sits

The default vault is ~/.compartment/memory.vault. Point somewhere else with --vault PATH on any command, or with the COMPARTMENT_VAULT environment variable.

That one file is the whole store. It opens with a header holding the format version and the Argon2id keyslots, then the sealed payload, then journal entries appended as new memories arrive. Each journal entry is framed with its length and a CRC (cyclic redundancy check) of that length, so a write cut short by a crash is recognised as a truncated entry instead of being read as data. Compaction serialises the vault, writes a temporary file, fsyncs it, then renames it into place, which means a reader never sees a half written vault.

The useful consequence: a backup script copies exactly one path. The awkward consequence: you cannot grep it, and you cannot repair it in a text editor. If you want memory you can read with cat and commit to git, Memmy's plain local memory files are the opposite trade. Both are reasonable, depending on whether the thing you fear is a stolen laptop or a tool that breaks.

compartment uninstall removes the software and keeps the vault. Pass --purge only when you mean it.

Wiring it into an agent

One command wires a supported client.

compartment integrate --list
compartment integrate claude

For Claude Code that writes an MCP (model context protocol) server entry and a PostToolUse hook into ~/.claude/settings.json, backing the file up first, installs a /compartmentalize skill under ~/.claude/skills/, and adds a managed block to ~/.claude/CLAUDE.md telling the agent that Compartment supersedes the file based memory it was using. Confirm both halves:

compartment hook status
compartment recent

To register the server by hand instead:

claude mcp add --scope user compartment -- \
    compartment --vault ~/.compartment/memory.vault --caller claude-code serve

Any other MCP capable host takes the same server with its own caller name.

{ "mcpServers": { "compartment": {
    "command": "compartment",
    "args": ["--vault", "/path/to/memory.vault",
             "--caller", "your-agent-name", "serve"] } } }

Give each host a different --caller value. It is the label the audit log records and the key that namespace grants are written against, so a shared name makes both useless.

If Claude Code has already been writing facts to its own memory files, compartment import-claude --dry-run shows what would move before anything moves. Read what Claude Code keeps in its memory files first, because importing a year of notes into a fresh vault is how a memory store fills up with things nobody meant to keep.

How fast is a local vault

These are the figures the project publishes for a personal sized vault. They come from its documentation, not from a run here.

ChartCompartment published local latency, milliseconds
The data behind this chart
[
  {
    "label": "Store one memory, end to end",
    "latency_ms": 40
  },
  {
    "label": "Embed one memory, bundled model",
    "latency_ms": 25
  },
  {
    "label": "Hybrid search, median",
    "latency_ms": 11.6
  },
  {
    "label": "Vector search at 20k records, p95",
    "latency_ms": 0.68
  }
]

Storing one memory is quoted at 40 ms, and a vector search across twenty thousand records at 0.68 ms for the 95th percentile. The project's argument for keeping memory local is arithmetic: a single network round trip to a hosted memory API often costs more than the 11.6 ms median of a full hybrid search here.

Two design details explain the search numbers. Below twenty thousand records, Compartment compares the query against every vector, so recall is exact by construction rather than approximate. Above that it switches to HNSW (hierarchical navigable small world), an approximate index that trades a little recall for speed. The vault also records the SHA-256 hash of the embedding model and refuses to open under a different one, because vectors from two different models can be compared without any error appearing, and the scores that come back mean nothing.

Backups, and the copy you can still open next year

A locked vault is one portable file, so moving it is a copy.

compartment lock
scp ~/.compartment/memory.vault other-machine:
compartment --vault memory.vault unlock

Lock it first. Copying while the agent is writing can catch a journal entry mid append, and although the CRC framing lets a reader skip that trailing fragment, the memory inside it is gone. compartment lock --sign seals the file with an Ed25519 manifest, so the receiving machine can verify the copy arrived intact without holding the passphrase.

Because the file is already sealed, ordinary cloud storage is an acceptable place for it. That is the point where encryption at rest pays you back directly: the backup target never sees a memory.

Two warnings. Shredding does not reach backups, so a record you crypto shredded today stays readable in last week's copy to anyone with last week's passphrase. And compartment export --plaintext writes the whole vault unencrypted, which is the right tool for migrating to something else and the wrong file to leave in ~/Downloads.

Keep few copies and date them. A memory store turns into a liability once nobody prunes it, which is the argument in why stale agent memories quietly poison retrieval.

Key handling, rotation, and the second factor

compartment rekey
compartment 2fa enable
compartment 2fa status

rekey changes the passphrase by rewrapping the master key in the keyslot of the current file. Old copies keep the old passphrase, because those bytes were sealed before the change and nothing reaches back to edit them. Rotate the copies too, or accept that a retired passphrase still opens something.

2fa enable adds a keyfile as a second factor, combined with the passphrase during key derivation, so both are needed to open the vault. That also doubles the number of things you can lose. Keep the keyfile off the machine that holds the vault.

For scripts and CI (continuous integration), the passphrase can arrive through the COMPARTMENT_PASSPHRASE environment variable, and unlock --passphrase-stdin reads it from a pipe. Prefer the pipe. An environment variable is readable by other processes owned by the same user, and it tends to end up in job logs.

The audit history is hash chained, and compartment audit verify walks it and reports the first broken link. Run it after any restore, because that is when a silently truncated file shows itself.

What happens when you lose the passphrase

Nothing happens, and that is the design. There is no reset, no recovery phrase, and no address to write to, because no copy of the key exists outside your memory and your optional keyfile. The vault stays a file of random looking bytes.

So the recovery plan is not a plan for the vault. It is a plan for the passphrase. Record it in a password manager on the day you run compartment init. Then test it: lock the vault, unlock it using only what you wrote down, and let an agent start filling it after that works.

Compartment or a memory server

Compartment is single machine by design. Sharing means copying the locked file, or exporting and importing, and there is no concurrent writer, so a laptop and a workstation pointed at one file will overwrite each other's work.

When several machines need the same memory at the same time, that is a server problem. A self-hosted Mem0 memory server on a VPS answers it: one endpoint, many clients, memory that outlives the laptop. The cost is worth stating plainly. That server runs a process which can read the data it stores, so your threat model now includes the VPS and everyone who can reach its API.

Choose by the loss you actually fear. If it is a stolen laptop or a vendor reading your notes, the encrypted local vault is the stronger answer. If it is an agent that forgets everything the moment you switch machines, the server is.

FAQ

What does Compartment's encryption actually protect?

It protects the file. The vault is sealed with XChaCha20-Poly1305, its keyslots are wrapped with Argon2id, and the embedding vectors are encrypted as well, so a stolen disk or an old backup yields bytes with nothing readable in them. It does not protect an unlocked vault on a running machine, because the master key sits in RAM while the vault is open, and it does not govern what an agent does with a memory after recall returns it as plaintext.

If Compartment is offline, are my memories private from my model provider?

Only until they are recalled. Storing and searching happen with no network, and the embedding model runs locally, so nothing leaves the machine at write time. At read time the agent receives plaintext, and if that agent is a hosted model, the memory enters the prompt and travels to the provider like the rest of the context window. Never store a credential as a memory.

What happens if I lose my Compartment passphrase?

The vault is unrecoverable, on purpose. Compartment generates no seed and no recovery phrase, and holds no credential you do not hold, so there is nothing to reset. Put the passphrase in a password manager, keep any 2FA keyfile off the machine that stores the vault, and confirm you can unlock a copy before the vault holds anything you would miss.

Can two machines share one Compartment vault?

Not at the same time. A locked vault is a single portable file, and the documented path is to lock it, copy it, then unlock it on the other machine with --vault. There is no concurrent access, so two machines writing to one file will lose memories. Run a memory server when you need that.

#agent-memory#compartment#encryption#offline#privacy