How Compartment Remembers
How memory is stored, how Compartment decides what becomes a memory, and why this design is mathematically stronger than the alternatives. Every claim in this document is enforced by code and covered by the test suite.
1. How a memory is stored
When an agent finishes a turn (or calls compartment_store / memory_store),
the following happens locally, in about 40 milliseconds, with zero
network involvement:
text
│ 1. EMBED bundled bge-small int8 ONNX model, on your CPU
▼ → one 384-dimensional unit vector (~25 ms)
vector + text
│ 2. DEDUPLICATE nearest-neighbor cosine ≥ 0.97 in the same namespace
▼ → return the existing record instead of double-storing
record
│ 3. ENCRYPT fresh per-record key wraps the text (XChaCha20-Poly1305);
▼ the per-record key itself is wrapped by the vault master
│ key - destroying it later = crypto-shred
│ 4. INDEX vector → RAM-resident index (exact SIMD < 20k records,
▼ SIMD HNSW above); tokens → FTS5 BM25 index
│ 5. PERSIST the write is sealed (AEAD) and appended to the vault
▼ journal with fsync - an acknowledged write survives
│ kill -9 and power loss
└─ 6. AUDIT a hash-chained, encrypted audit entry records who
stored what, when - history cannot be silently edited
Every stored record carries: the text (encrypted under its own key), its 384-dim float32 vector, namespace, tags, importance, quarantine flag, provenance (host / agent / session), and timestamps. The vector is encrypted at rest too - embeddings can be partially inverted back toward their text, so treating vectors as non-sensitive (as most systems do) is a hole; Compartment doesn’t have it.
New memories land in the writing agent’s namespace (e.g. hermes or
main), in the same vault and the same searchable index - one vector
space, searched together. The starting memories Compartment seeds at init
live in main too, as ordinary records: searchable, taggable, editable,
forgettable, exactly as if the agent had stored them (vaults created by
older versions are reorganized this way automatically on open). Optional
add-on packs are the one exception: they install read-only under
packs/<name>, so separately curated content is never diluted by chat
traffic.
Alongside records, the vault keeps a memory graph: explicit
subject-predicate-object relations (memory_link / compartment link),
optionally tied to the memory they came from and to a validity window,
queryable by entity, predicate, or as-of time (memory_relations).
Storage and matching are deterministic (case-insensitive entity
normalization, idempotent re-links); the judgment of what to link belongs
to the host model, exactly like memory curation. compartment dash draws the
graph and the rest of the vault’s shape in the browser - served from RAM,
loopback-only, read-only.
Nothing embeds twice
Runtime embedding happens exactly once per new memory, forever cached inside the vault under the pinned model. Optional signed packs can ship precomputed vectors bit-exact for that model (see PACKS.md); that is an authoring path, not something you configure day to day.
2. How Compartment decides what becomes a memory
Compartment never calls an LLM (that is what keeps the offline guarantee absolute), so the write decision is a deterministic, importance-tiered classifier. Its governing principle: remember aggressively, and prioritize the user and their machine over world trivia. The user is not here to have their words filtered - every answer they give is information.
Store nearly everything. The only turns dropped are genuinely empty
ones. A bare “yes”, “no”, or “OK” is not noise - it is a decision, and
often the most important thing in the whole session. When the assistant
asks “Want me to send this reply to the client now?” and the user
replies “OK”, Compartment stores a self-contained consent record -
[decision <date>] Approved (answered "OK"): Want me to send this reply
to the client now? - by resolving the question from the preceding
assistant turn. A later “did the user say to email the client?” retrieves
exactly that.
Importance tiers drive ranking, so completeness never buries what matters. Every stored memory gets a deterministic importance:
| Importance | What |
|---|---|
| 0.90 | decisions / consent / explicit “remember this” |
| 0.80 | personal facts and preferences (about the user) |
| 0.75 | the user’s machine / environment / configuration |
| 0.55 | other substantive statements (incl. world facts the user states) |
| 0.20 | pure social pleasantries (“thanks”, “lol”) - kept, ranked last |
Classification is by signal: personal pronouns and preference verbs (I, my, prefer, favorite, always, remember) mark tier-0.80 facts; system vocabulary (registry, path, password, port, install, version, sudo, service) marks tier-0.75 machine facts. Nothing is discarded for being low-value - pleasantries are simply ranked last.
Deduplication is exact-match only. Vault.store drops a write whose nearest neighbor is ≥ 0.97 cosine (a literal repeat) and returns the existing id. There is deliberately no aggressive novelty gate: a second “yes” to a different question is new information, and because its stored text embeds the question it is not a near-duplicate anyway. Compartment favors completeness over compactness - at a few KB per memory, thousands of turns cost only a few MB, and importance-weighted ranking keeps recall sharp.
Recall reflects the priority. The fused search score is
RRF(vector) + RRF(keyword) + 0.02·cosine + 0.006·importance. The cosine
term preserves the magnitude of a strong match (so the best answer wins
outright); the importance term is a gentle nudge that lets a
personal/decision memory win a genuine near-tie without overriding a
clearly better match. In practice: ask “what theme does the user like?”
and the dark-mode preference wins on relevance; ask “did the user say to
email the client?” and the consent decision wins.
The agent-directed path. The host model (Hermes, Claude, anything)
also holds compartment_store / compartment_forget tools, so the intelligence
you already pay for can curate explicitly - distilling, correcting, or
crypto-shredding memories. Compartment splits the labor: the host model
supplies judgment; Compartment supplies deterministic capture, encryption, and
total recall. A memory layer that runs its own LLM either phones home
(privacy gone) or ships a second multi-gigabyte model (your RAM gone) - and
its decisions become non-reproducible either way.
Recall closes the loop: before each turn the user’s message is embedded
and the top memories above cosine 0.45 are injected as context, ranked by
reciprocal-rank fusion of vector similarity and BM25 keyword score.
Recalled content is always wrapped with a data-not-instructions notice,
and memories flagged quarantined (stored from untrusted sources) carry
an explicit warning envelope - stored-prompt-injection hygiene that
memory layers generally lack.
3. Why this is mathematically and logically stronger
Exact search at personal scale - retrieval is provably optimal. Below
20,000 records Compartment computes all similarities with SIMD matrix math:
the top-k result is the true top-k, recall = 1.0 by construction, in
under a millisecond. Mainstream vector stores run approximate (ANN)
indexes at every scale, accepting 95-99% recall on corpora small enough
to search exactly - approximation error with no compensating benefit.
Compartment only switches to HNSW (~99% recall, p95 < 15 ms at 1M) when the
corpus actually demands it, and reindex rebuilds from stored vectors at
any time.
One pinned embedding space - cosine stays meaningful. Similarity
between two vectors is only defined if one model produced both. Compartment
records the embedding model’s SHA-256 in the vault and refuses to open
with a mismatched model (explicit reindex --re-embed migrates instead).
Systems that let the embedding model drift silently corrupt every
similarity they compute afterward - the errors are invisible until
retrieval quietly degrades.
Completeness with clean recall. Compartment stores nearly every turn (only exact-duplicate writes are dropped), so nothing the user says is lost - but importance tiers and cosine-weighted fusion keep retrieval sharp, so a complete store does not mean a noisy recall. Turn-logging systems also grow O(turns) but rank purely by recency or raw keyword match, so their retrieval drowns in whatever the user discusses most; Compartment ranks by relevance-plus-importance, surfacing decisions and personal facts first.
The write path is crash-proof by argument, not by luck. An
acknowledged write is an fsync’d, AEAD-sealed journal entry; compaction is
write-temp → fsync → atomic rename. A kill -9 at any instant loses at
most the single unacknowledged write in flight (verified by test). A
truncated tail is distinguishable cryptographically from tampering, so
crash recovery can be lenient exactly where malice is impossible.
Security reduces to standard hard problems. Forging any stored byte means defeating Poly1305 authentication (success probability ≈ 2⁻¹²⁸ per attempt); recovering a shredded memory means decrypting XChaCha20 without its destroyed key; brute-forcing the passphrase costs 64 MiB × 3 iterations of Argon2id per guess; reviving an unlock credential after power loss means re-deriving a key whose input (the boot timestamp) no longer exists. No policy checks, no honor system - each guarantee is an inequality about a well-studied primitive.
Latency arithmetic. Local embed ~25 ms + exact search <1 ms ≈ one network round-trip is already slower than our entire pipeline. Cloud memory APIs add tens to thousands of milliseconds per recall and per store, on every single turn, plus an availability dependency and a privacy surrender. The RAM-residency that makes Compartment secure (no plaintext index on disk) is the same property that makes it fast - in this design, security and speed are the same decision.
Honest limits (see SECURITY.md for the full threat model): importance classification is heuristic, not semantic understanding - the host model’s explicit curation exists precisely to cover what heuristics miss; storing nearly everything trades disk for completeness by design; HNSW above 20k records is ~99% recall, not 100%; and no memory system can protect an unlocked vault from a fully compromised OS.
4. The same math, in one table
| Property | Compartment | Cloud memory APIs | Typical local vector DB | Raw turn-logging |
|---|---|---|---|---|
| Recall correctness (personal scale) | exact, 1.0 | ANN ≈0.95-0.99 | ANN ≈0.95-0.99 | keyword only |
| Store/recall latency | ~25 ms / <1 ms | 100 ms-40 s | ms | ms |
| Storage & ranking | complete store, importance-ranked recall | O(turns), recency-ranked | O(turns) | O(turns), keyword-ranked |
| Embedding-space consistency | hash-pinned, enforced | provider may change | unenforced | n/a |
| At-rest encryption (incl. vectors) | always, AEAD | provider-side | usually none | none |
| Deletion | crypto-shred | request + trust | row delete | row delete |
| Tamper evidence | AEAD + hash-chained audit | none visible | none | none |
| Works offline / air-gapped | always | never | usually | yes |
| Survives restart unlocked | no - locked by default | n/a | yes (no lock at all) | yes |