Compartment
About
Fully offline, encrypted-at-rest memory for AI agents, with the embedding vectors encrypted too, exact RAM-resident search, crypto-shred deletion and a hash-chained audit log.
Details
- Author
- maxfreedompollard
- Categories
- AI, Security, Other
Jump to
Setup
Install Compartment in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/maxfreedompollard/Compartment
Follow the installation instructions in the repository README, then restart your MCP client.
Listed on:PyPI·Glama·LobeHub·MCP Toplist·mcpservers.org·TensorBlock·Libraries.io·Snyk Advisor·deps.dev
Durable agentic memory, encrypted at rest.
Your AI agent forgets you the moment the session ends. Compartment ends that. With Compartment, your AI agent gets better with experience: it notices and permanently stores every decision, preference and detail offline, encrypted, to your own computer. Hermes, Claude, OpenClaw and other AI Agents can install in one command. One fully-transferable memory store is shared simultaneously by all agents on the computer. 100% offline: no network, no API key, no cloud account, no telemetry. The embedding model ships inside the package, and a full search returns in about 12 ms, beating the round-trip a hosted memory charges you for. Every byte at rest is AEAD-encrypted, the embedding vectors included, and only your passphrase opens it.
Unlike other agentic memory, we offer an option to start off with memory - 6,718 curated facts seeded at install: the physical constants and unit conversions, 800+ hardware facts with real specs (Apple silicon, PCs, CPUs and GPUs, phones, game consoles, Raspberry Pi, storage, displays, connectors) to provide a map of computer geography, operating system versions and release names, network ports and HTTP, file signatures, character encodings, shell and Unix internals, git, regex, SQL and hashing, ISO country, currency and time codes, and more. Allows an offline agent to operate better without internet, and an online agent to operate faster and more accurately.
More secure, by construction.Every byte at rest is authenticated-encrypted, the embedding vectors included (most tools leave those in the clear, and vectors can be inverted back toward text). Deletion is cryptographic: destroy the record's key and it is gone, unrecoverable. Tampering is detected, history is hash-chained, and the vault locks itself on restart or power loss. It runs fully offline: a runtime guard aborts on any network attempt, and CI proves it on three operating systems.
Not one step harder.One command installs it, creates the vault, and wires your agent. No API key, no cloud account, no daemon. Unlock when you want to use it; lock when you want it closed. By default an unlock stays open for weeks (until restart or you lock it), like any app you leave running. The security is free at the point of use because it falls out of the architecture, not out of your patience: keeping plaintext off disk forces the index into RAM, and a RAM-resident index is also the fastest one there is. Secure and fast are the same choice here, and neither costs you a configuration step.
One line install, works on all operating systems.
pip install compartment && compartment init
claude,hermesandopenclaware the three auto-connect targets. Each one also gets the/compartmentalizeskill installed into its own skills directory.
/compartmentalizesaves the conversation before it is thrown away.Every agent eventually compacts or summarizes a long session, and the summary is written by a pass that has no tools, so nothing can be stored from inside it: whatever the model did not think to save is simply gone. Type/compartmentalizeand the whole conversation is swept into the vault first - people and contacts, credentials and where they live, URLs and hosts, decisions and the reasoning behind them, and a narrative of the session itself. Then compact, and nothing is lost. It works on its own at any point too.
One click install (for people not good with command line).DownloadCompartment.pkgfrom thelatest releaseand open it. Python, the embedding model and every dependency are inside it. macOS only.
After install, everything is managed from the app: themenu baron macOS, thenotification areaon Windows, and awindowfrom your applications menu on Linux.
Compartment is an MCP server, so it works with all MCP capable agentic AI out of the box. Every option is inConfiguration.
- One command installs it, creates the vault, and wires your agent. No API key, no cloud account, no daemon.
- On the Mac, open one.pkgand you are done. Python, the embedding model and every dependency are inside it.
- An app runs the whole thing without a terminal on all three systems: vault state, unlock, lock, and the last five memories it saved. The macOS menu bar, the Windows notification area, a window on Linux.
- Every feature toggles in that panel instead of a config file: model-independent capture, starter facts in search, auto-lock.
- /compartmentalizeis installed into every agent it connects, so one command banks a whole conversation before compaction throws it away.
- Your vault ships full. The 6,718 seeded facts are ordinary memories, editable and forgettable, and one switch keeps them out of search.
- Runs under what you already use: Hermes ("no setup needed"), Claude Code and Desktop over MCP, OpenClaw, every MCP client, plus a CLI for scripts and cron.
- "OK" is a decision, and Compartment files it as one, with the question it answered. That is the record you need later.
- Decisions beat preferences, preferences beat machine details, machine details beat chatter. A fixed ranking, not a model's mood.
- It forgets nothing. Small talk is kept and ranked last.
- It replaces your host's built-in memory instead of fighting it: imports what Claude Code already wrote, then supersedes it.
- It captures even when the model does not cooperate. A hook writes the fact whether or not the model calls the tool.
- A graph, not a pile. Explicit relations with validity windows answer who worked where, and when.
- 0.68 ms vector search. About 12 ms for the full hybrid pipeline. A cloud memory spends longer than that saying hello.
- Exact below 20k records: recall = 1.0 by construction, not an approximation.
- Hybrid always: meaning and keywords, fused.
- One pinned embedding space, enforced every time the vault opens, so your comparisons stay valid forever.
- Every byte at rest is AEAD-encrypted, embedding vectors included. Most tools leave vectors in the clear, and vectors invert back toward text.
- Only your passphrase opens it. Compartment generates no password, no seed, no recovery phrase, and holds no credential you do not.
- Add a keyfile and unlock takes two factors. Both feed Argon2id together, so it is arithmetic, not a policy check.
- forget --shreddestroys the record's key. The content is mathematically unrecoverable, not marked deleted.
- Restart or power loss locks it, and the agent has a panic lock that clears every credential instantly.
- 100% offline. A runtime guard aborts on any network attempt, and CI proves it on Linux, macOS and Windows. Zero open ports. No telemetry, ever.
- No LLM inside. Embeddings run locally in under 300 MB, and judgment stays with the model you already pay for.
- Tamper-evident: hash-chained audit log, sealed journal, verified kill-9 crash recovery.
- One portable file. Move a locked vault anywhere, andlock --signseals it with an Ed25519 manifest anyone can verify without a credential.
- compartment dashputs the entire vault on a local page: 127.0.0.1 only, random token, read-only.
Full write-path, decision math, and comparisons indocs/MEMORY.md. The load-bearing ideas:
Nearly everything is stored; nothing important is buried.Only empty turns are dropped. A bare "OK" is not noise, it is a decision: when the agent asks"Want me to send this reply to the client now?"and the user answers"OK", Compartment resolves the question from the conversation and stores[decision 2026-07-20] Approved (answered "OK"): Want me to send this reply to the client now?at the top importance tier. Asking"did the user say to email the client?"later retrieves exactly that record.
Deterministic importance tiers rank recall: decisions/consent 0.90, personal facts and preferences 0.80, the user's machine and configuration 0.75, other substantive statements 0.55, pleasantries 0.20 (kept, ranked last). Importance multiplies a match rather than adding to it, so it settles near-ties in favour of what matters and can never surface a memory for a question it has nothing to do with. The whole scoring model, and the numbers it was chosen against, are inThe mathematics. The agent learns the user and the computer first, the world second, and forgets nothing.
One memory, not two.Agent hosts increasingly ship a memory of their own - Claude Code keeps per-project Markdown files with an auto-loaded index. Two memories means facts land in whichever one the model happened to think of, and neither is complete. Compartment takes over on install: it imports what the file memory already holds, and both the MCP handshake and the managed CLAUDE.md block tell the model that Compartment supersedes it - write every new memory here, treat the files as a read-only archive. One vault, encrypted, shared by every agent and project on the machine. Nothing is deleted; the files stay exactly where they were.
Capture that does not depend on the model.Instructions are a request, and a host that declares its own memory in its system prompt outranks anything a tool says. Socompartment integrate claudealso installs aPostToolUsehook: when Claude Code writes a memory file, the fact lands in the vault whether or not the model ever thought about Compartment. The hook is additive and idempotent (your other hooks are untouched, settings.json is backed up first), it exits successfully no matter what - a memory tool must never break your editor - and it stays quiet when the vault is locked.compartment hook status | install | uninstall, orintegrate claude --no-hooks.
An app on all three systems, from the same one install.pip install compartment && compartment initsets it up on macOS, Windows and Linux. There is no separate package, no extra to remember and no second command. On the Mac you can instead openCompartment.pkgfrom thelatest release, which carries Python and every dependency inside it, so there is nothing to install first.compartment init --no-appskips the app for headless boxes and CI.
The same panel, in the place each system keeps things like this: themenu baron macOS, thenotification areaon Windows, and onLinuxan ordinary window, with Compartment in your applications menu. Linux gets a window rather than an icon deliberately. Whether a tray icon appears there depends on the desktop, and on GNOME or Wayland it can simply never show up with nothing said, which is the worst way for the control that unlocks your memories to fail.
The panel shows whether the vault is open, how much it has learned, the three settings worth changing day to day (capture hook, whether starter facts join searches, auto-lock), which agents are connected and buttons to connect them, and the last five things it remembered. Unlock, lock and change your passphrase there too, without opening a terminal. It holds no vault in memory - state comes from the CLI, so an idle app costs nothing.
See what it just learned.compartment recentlists the newest memories, newest last, hiding the thousands of seeded starting facts so the handful that real use produced are actually visible - andcompartment statusreportsorganic_recordsbeside the total, so a vault that has learned nothing can never look busy. Same view over MCP asmemory_recent.
One fact per memory, dated.A memory is an atomized data point, not a session log.memory_store_manytakes a whole batch in one call, so storing six facts separately costs the same one round trip as bundling them into a longer format description - which is what made agents write longer format descriptions. Every memory carries the moment it was saved, and separately the day the fact was discovered together with how it was established, appended as a short[web search, 2026-08-01]clause. Those are two different dates: a price you check on the Friday and write up on the Monday keeps Friday as its discovery and Monday as its save.
Search returns what is relevant, not a fixed number.How many memories answer a question is a property of the question, so Compartment returns every memory whose evidence stands up against the best answer to that same question, capped generously. The cut has to be relative, because scores are not comparable between questions - on a real vault the nonsense query "how to bake sourdough bread" scored higher than the genuine "what did Max decide about Airtable". Ask it something the vault knows nothing about and it returns nothing at all rather than a page of polite irrelevancies. Pass an explicittop_kwhen you want exactly that many.
Tags that stay true.What a memory is about never changes. What it is relevant to changes constantly, and a tag written once, on the day the memory was stored, cannot know that.
Here is an example of the failure this solves. Working on a project called Northwind, you learn that your client wants figures before conclusions: never open with the recommendation, open with the numbers. That is a durable fact about a person. The agent stores it and tags itnorthwind,reporting, because Northwind is what was in front of it that day.
Northwind ends. Two years later the same client, now going by the name Harbour, hires you again. Your agent narrows recall toharbour, the way anyone narrows a search once a vault holds thousands of memories. The one thing you most want applied is filed under a name that no longer exists. It is still true and still exactly the right rule, but a tag-filtered search cannot return it, because tag filtering is a subset match and a memory lacking the tag is simply not in the set. The memory did not decay. Its index entry did.
Compartment repairs that automatically, offline, in the background, without using an LLM. As Harbour memories accumulate - the client asking for numbers up front again, a deck reordered to lead with them - they land by design beside that old preference in embedding space, because they are about the same subject and placed similarly through ordered logic. A background pass gives every memory the tags its nearest neighbours carry, weighted by cosine, and the preference picks upharbourfrom them. Two more offline signals run alongside it: tags that nearly always occur together come to imply one another, and any existing tag whose phrase appears in a memory's own text is attached. Nothing in Compartment ever knew what Northwind or Harbour were.
The pass can only write the tags column, never the text, the dates or the embeddings. It is additive unless you pass--prune,tags_originkeeps the tags a memory was born with forever, andcompartment retag --dry-runshows exactly what would change before anything does.
One pinned embedding space.The model's SHA-256 is recorded in the vault and enforced at open; cosine comparisons stay mathematically valid forever instead of silently degrading when a model changes. Migration is explicit:compartment reindex --re-embed.
No LLM inside.Embeddings run locally (bundled 384-dim int8 ONNX model, <300 MB RAM). Judgment belongs to the host model you already run, viamemory_store/memory_forget; Compartment contributes deterministic capture, encryption, and total recall. That split is what makes the offline guarantee absolute and every decision reproducible. Pair Compartment with an offline LLM and the whole agent stack can run usefully with no network at all.
Everything below lives in one file,src/compartment/ranking.py, which the vault, the dashboard and the benchmark all import. A benchmark score is therefore a measurement of the product and not of a copy of it that has drifted.
Storage: a memory is embedded in windows, not truncated
The encoder reads 512 tokens. Text past that is not weighted less, it is not seen at all, so a long memory used to be searchable only by its opening. On a real 6,705-memory vault, 40% of records ran past the window and57.6% of the whole corpus was invisible to semantic search.
So a record is embedded as overlapping windows ofW = 448tokens at a stride ofS = 384, giving 64 tokens of overlap so no fact is cut in half by a boundary, and the record is scored by its best window:
windows(d) = ceil( max(0, tokens(d) - W) / S ) + 1 capped at 64 s_vec(d) = max over windows w of d : cos(q, w)
Max-pooling, not averaging: a memory is relevant ifanypart of it is, and an average would punish a long memory for the parts that are about something else. With one window per record it reduces exactly to the old behaviour, so it can never be worse for a short memory. The cost is small because most memories are short: on that vault, 6,705 records produced 6,785 windows.
Windows are measured in model tokens, never characters. A character budget is wrong by a factor of three between prose and a hex digest, and being wrong here means silently dropping the end of a memory.
Recall: two channels, combined as evidence rather than added
Two indexes look for a memory and they answer different questions. The vector index answerswhat does this mean. The keyword index answerswhat does this say. Their scores are not denominated in the same thing, and combining them is the entire difficulty.
The obvious move, and what Compartment shipped until now, is to add them. Adding is the wrong operation: it lets a merely-good semantic match outvote conclusive literal evidence. Searching a real vault for a commit sha occurring in exactly one memory out of 6,705 returned that memorybelow ten paraphrases of it- the keyword index had ranked it first and the sum buried it.
The two channels are not addends, they arealternatives: either one alone can establish relevance. That is a soft OR over independent evidence,
P(relevant) = 1 - (1 - p_vec)(1 - p_lex)
and the score is its logarithm, which ranks identically while continuing to spread results apart near the top instead of saturating at 1:
score(d) = - w_vec · log(1 - p_vec(d)) - w_lex · log(1 - p_lex(d)) w_vec = 0.75 w_lex = 0.25
Either channel approaching certainty carries the memory on its own, and neither can veto the other.
Reading a cosine as a probability.An L2-normalized encoder gives cosines that are comparableacrossqueries, so they map through fixed bounds. Per-query min-max normalization is the obvious alternative and it is a trap: it rescales the best hit of a hopeless query up to 1.0 and throws that calibration away.
p_vec(d) = clamp( (cos(q, d) - 0.25) / (0.85 - 0.25), 0, 0.88 )
That ceiling of 0.88 is doing real work. A cosine is a similarity, never an identity: an encoder can saythis is about the same thing, but it can never saythis is the record you named. A literal match on a string unique to one memory can say exactly that. So the semantic channel is capped below the certainty the literal channel may reach, and the bound is forced rather than chosen - the literal channel tops out at0.25 · -log(1 - 0.999) = 1.727, so the cap must satisfy0.75 · -log(1 - cap) < 1.727, givingcap < 0.90.
Reading a keyword hit as a probability, and deliberately not with BM25.BM25 answershow well does this match, which is not what settles a contest against a semantic hit. What settles it is how unlikely the match was by chance. So each query term carries its self-information over the vault, and a memory scores thefraction of the query's information it accounts for:
I(t) = log( N / (1 + df(t)) ) N = records in the vault p_lex(d) = ( Σ I(t) for query terms t present in d ) / ( Σ I(t) for all t )
A term unique to one memory is near-conclusive evidence. A term appearing in a tenth of the vault is nearly none, whatever its BM25 happens to be. This is the piece that makes a literal hit and a semantic hit comparable at all.
The keyword index is queried as AND first, since an exact phrase match is the strongest signal available. FTS5's implicit AND means a nine-word question has to appear word for word, so when AND finds nothing it falls back to OR over only the terms carrying information - anything appearing in more than 10% of records is dropped. That ceiling is measured from the vault rather than taken from an English stopword list, so it behaves the same for a vault full of code, of names, or of another language.
A small rank-agreement residue is added, the one thing reciprocal-rank fusion is genuinely good at, sized to break ties rather than decide them:
+ w_rrf · k · [ 1/(k + rank_vec) + 1/(k + rank_lex) ] w_rrf = 0.10, k = 20
Importance ranking: priors multiply, they never add
final(d) = score(d) · ( 1 + w_imp · (2·importance(d) - 1) + w_rec · 2^( -age_days(d) / 180 ) ) w_imp = 0.15 w_rec = 0.10
Multiplicative, so a prior can only reorder a memory that already matched.An additive prior lets a very important memory surface for a question it has nothing to do with, which is how a memory system starts feeling haunted. A memory that matched nothing scores zero, and nothing can lift it off zero.
Centred on the 0.5 default, which is why2·importance - 1appears rather thanimportance. Every unweighted memory carries 0.5, including the thousands of starting facts a vault ships with. Uncentred, they all collect the same silent boost, which is another way of saying importance did nothing at all. Centred, an unweighted memory is exactly neutral and a deliberate weight is the only thing that moves.
The tiers the capture path writes: decisions and consent 0.90, personal facts and preferences 0.80, the user's machine and configuration 0.75, other substantive statements 0.55, pleasantries 0.20. Recency halves every 180 days.
Retrieval order, and why the pool is wide
Namespace, tag, date and starter-fact filters runafterranking, so a candidate pool sized to the number of results requested can be emptied by them while matching memories sit just past the cut. The pool starts at 200 per channel and widens up to three times when filtering leaves too few.
Against the previous scorer, end to end throughVault.search, on a real 6,705-memory vault with 44 queries in four families:
Nothing regressed in any family. The weights were chosen from a sensitivity sweep and are deliberately round: the result is flat around them, because a ranker that only works atw_lex = 0.37is a ranker that does not work.
One command per platform. Each installs the package, creates your encrypted vault, and wires the agent.
Every one of these is also a button in the app. Click the Compartment icon in your menu bar or notification area, and underCONNECT AN AGENTpress Claude, Hermes or OpenClaw. The button runs the samecompartment integratecommand for you, so nobody has to open a terminal a second time.
Claude (Code + Desktop)- macOS / Linux:
pip install compartment && compartment init && compartment integrate claude
py -m pip install compartment; compartment init; compartment integrate claude
Registers the MCP server with the Claude Code CLI (user scope, all projects),imports any memories Claude Code already wrote to its own file-based memory(copy-only - the Markdown files are never modified;--no-importopts out,compartment import-claudedoes it later), and prints the Claude Desktop config block. The server describes itself over the MCP handshake - it tells the model to recall before answering and to store durable facts, credentials, names, and decisions - so Claude treats Compartment as its memory with no hand-written instruction;integrate claudealso writes a managed, idempotent block into your CLAUDE.md as backup.
pip install compartment && compartment init && compartment integrate hermes
py -m pip install compartment; compartment init; compartment integrate hermes
Installs the provider plugin, wires the Hermes venv, and runshermes memory setup compartment. Compartment then appears in thehermes memory setuppicker beside hindsight and mem0, the only entry marked"no setup needed": no API key, no cloud account, no daemon. Verify withhermes memory status. See everything Hermes remembers at any time withcompartment dash- one command, and the vault opens in your browser (memories by kind, growth, the relation graph, live search); Ctrl-C closes it.
Hermes also reads the portableAgent Pluginsformat, and this repository is one. That route installs the MCP server and the/compartmentalizeskill straight from GitHub, and wants Hermes 0.20.0 or newer, which is where the portable plugin loader arrived:
pip install compartment && compartment init hermes plugins install MaxFreedomPollard/Compartment hermes plugins enable compartment
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.




