Compartment

MaxFreedomPollardApache-2.0活跃维护

给 AI 智能体用的加密离线长期记忆库,一键安装,带记忆图谱界面,支持各类操作系统和主流 Agent,让记忆的生成、存储和检索更高效。

Encrypted, fully offline agentic memory. One click install, GUI w/ memory map, all OS and agents. Superior memory creation, storage and retrieval.

v4.14.0发布于 2026-11-07 21:27 UTC569star8fork同步于 2026-11-10 11:58 UTC

怎么上手

由 README 提炼,执行前请确认
pipx install compartment && compartment init
硬件要求按 README 说明

README 未说明显存要求;依赖 onnxruntime,macOS 14+、Windows 10/11、glibc 2.28+ 的 Linux 均可运行,支持 x86_64、aarch64(含树莓派 5)与 Apple silicon,Windows 需装 VC++ 运行库。

支持 Apple Silicon有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

给 AI Agent 用的加密离线记忆库,一个本地 vault 让所有 MCP 客户端共享记忆

给 AI 智能体用的加密离线长期记忆库,一键安装,带记忆图谱界面,支持各类操作系统和主流 Agent,让记忆的生成、存储和检索更高效。

核心能力

  • 每条记忆只存一个断言,超过 200 字符会被拒绝
  • 混合向量与关键词检索,内存索引约 12ms 返回结果
  • 磁盘内容含嵌入向量全部加密,仅凭口令解锁
  • 内置嵌入模型,新库自带约 6700 条参考事实
  • 一条命令把 Claude Code、Hermes、OpenClaw 等接入

容易踩的坑

Debian/Ubuntu/Fedora/Arch/Homebrew 拒绝系统级 pip install,需用 pipx 或 uv;Linux 图形界面需要 Tk(如 python3-tk);Alpine、FreeBSD、Termux 需先装系统 onnxruntime 并用 --system-site-packages 建虚拟环境;WSL 2 空闲后解锁凭据失效,vault 会重新上锁

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v4.14.0
  1. v4.14.0Compartment 4.14.0最新正式版2026-11-07 · 2026-11-07 21:27 UTC

    Compartment 4.14.0 upgrades associative memory recall and adds zero-effort encrypted vault backup and multi-machine merge across the macOS, Windows, and Linux GUIs and CLI — 100% offline, with zero network calls of any kind and zero plaintext ever touching disk.

    Downloads

    FileFor
    Compartment-4.14.0.pkgmacOS on Apple silicon, one click, with the menu bar app
    Compartment-4.14.0.dmgmacOS on Apple silicon, drag to Applications
    Compartment-4.14.0-intel.pkgmacOS on Intel, one click
    Compartment-4.14.0-intel.dmgmacOS on Intel, drag to Applications
    compartment-4.14.0.mcpbClaude Desktop extension (attached a few minutes after publishing)
    compartment-4.14.0-py3-none-any.whlpip and offline installs, any OS
    compartment-4.14.0.tar.gzsource
    SHA256SUMS.txtchecksums of all of the above
    pipx install compartment && compartment init      # or: uv tool install compartment
    

    What's New

    One-Click Encrypted Backup & Lossless Multi-Machine Merge

    • One-click encrypted .vault backup in macOS, Windows, and Linux GUIs (Back up…) + CLI (compartment backup): Exports a verified FORMAT.md v1 encrypted image (NUCV, Argon2id + XChaCha20-Poly1305, Ed25519 manifest check when signed) while locked or unlocked, without prompting for a passphrase or writing plaintext to disk. Automatically strips any unacknowledged crashed journal tail, writes atomically at mode 0600 with fsync, and re-parses + SHA-256 verifies the finished file.
    • One-click lossless encrypted .vault merge in macOS, Windows, and Linux GUIs (Merge…) + CLI (compartment merge): Reconciles another machine's .vault backup strictly in RAM without modifying the backup file. Same-key backups merge with zero passphrase prompt; independent backups prompt via stdin only. Reuses stored vectors directly when model_sha256 matches (zero ONNX inference), re-embeds offline when models differ, skips duplicate starter memories in O(1), and merges affirmed timestamps, descriptive tags, importance, superseded_by tombstones, and relations.
    • Portable organic & graph JSONL (--organic-only, --include-relations): compartment export can exclude starter/pack records and include relations edges; compartment import restores relation edges, remaps src_id links, and preserves provenance.

    Associative Recall & Multi-Project Relevance

    • 1-hop relation-graph spreading activation in search: Queries now probe indexed relations (rel_subject, rel_object, rel_src) within the caller's allowed namespaces and active [valid_from, valid_to] windows, surfacing memories linked via src_id, co-linked entities, or rare 1-hop neighbor entities even with zero lexical overlap — calibrated in log-probability space (W_GRAPH = 0.55) strictly below direct literal and saturated semantic matches.
    • Bidirectional camelCase / PascalCase ↔ snake_case identifier recall: FTS indexing and information_coverage match split and compound code identifiers in both directions (maxMemoryChars ↔ max_memory_chars ↔ max memory chars) without altering _tokens() or matching substrings inside ordinary words (key never matches keyboard).
    • Multi-query faceted search (queries): Vault.search and MCP memory_search accept optional decomposed queries: list[str], batch-embedding all facets in a single encoder/daemon pass (embed_queries) and fusing evidence by per-record maximum so multi-part prompts never dilute into one averaged vector.
    • Zero-config cross-OS git project context prior: Stores from the MCP server, CLI, and Claude hooks automatically detect normalized owner/repo slugs from .git/config (including worktrees and submodules, in pure Python with zero subprocesses) and apply a gentle ordering tie-break (W_PROJECT = 0.08) to fused scores when working inside the matching repository without inflating static membership floors.
  2. v4.13.0Compartment 4.13.02026-10-20 · 2026-10-20 18:12 UTC
    查看更新说明

    Compartment 4.13.0 is a compatibility release: it is about Compartment installing, starting and staying connected on every system and in every agent, including the places it used to fail quietly - agents' sandboxed shells, Windows on ARM, Intel Macs, containers, and GUI apps that cannot see your PATH. 4.12.0 was merged but never published, so its 79 stability fixes ship here too.

    Downloads

    FileFor
    Compartment-4.13.0.pkgmacOS on Apple silicon, one click, with the menu bar app
    Compartment-4.13.0.dmgmacOS on Apple silicon, drag to Applications
    Compartment-4.13.0-intel.pkgnew: macOS on Intel, one click
    Compartment-4.13.0-intel.dmgnew: macOS on Intel, drag to Applications
    compartment-4.13.0.mcpbnew: Claude Desktop extension (attached a few minutes after publishing)
    compartment-4.13.0-py3-none-any.whlpip and offline installs, any OS
    compartment-4.13.0.tar.gzsource
    SHA256SUMS.txtchecksums of all of the above

    Everything here was built from this tag on GitHub's machines, and PyPI carries the very same wheel and sdist.

    pipx install compartment && compartment init      # or: uv tool install compartment
    

    The Mac app needs macOS 13.4 or newer. The README's new Where it runs table gives every system, CPU and Python version CI tests.

    Highlights

    • Works from an agent's own shell. Claude Code and Codex run commands in a sandbox that blocks the macOS boot clock, Unix sockets and some file modes. Every compartment command an agent ran there used to fail with "Cannot identify this boot session". It now uses the vault you unlocked, embeds in-process, and never hangs.
    • Clients can always start the server. compartment integrate writes the full path of the installed command. GUI apps started from the Dock or Start menu do not inherit your PATH, and a bare compartment failed there with "spawn compartment ENOENT".
    • pip install compartment works on Windows on ARM and Intel Macs again. Two indirect dependencies (cryptography, argon2-cffi-bindings) stopped shipping wheels there, and pip tried to compile them. A new CI check fails if any supported platform would need a compiler again.
    • Intel Mac installer. The Mac app is built for Apple silicon and Intel, and no longer bundles libraries that need a newer macOS than it says it supports.
    • A plugin in every store.
      • New: a Claude Desktop extension (.mcpb).
      • Updated: the Claude Code plugin now carries a version, plus better Codex and MCP Registry listings, and corrected Gemini CLI and LM Studio listings.
      • Ready to submit: Docker MCP Catalog and Continue entries.
      • The full list is in the new docs/PLUGINS.md.

    Agents and clients

    • 33 clients, adding Kiro, Amazon Q Developer CLI, Junie, Factory and Crush. Continue gets its own block file, and Goose gets a real YAML extension entry.
    • New config locations: Claude Desktop's Microsoft Store build, Cline 4, Kilo Code 7, Cline/Roo/Kilo inside Cursor, Windsurf, VSCodium and Flathub VS Code, and Zed on macOS.
    • Moved config homes followed: CODEX_HOME, GEMINI_CLI_HOME, QWEN_HOME, COPILOT_HOME, OPENCODE_CONFIG_DIR, GOOSE_PATH_ROOT, CLAUDE_CONFIG_DIR, and OpenClaw's state and config variables.
    • OpenClaw: the server is written under mcp.servers, where current OpenClaw reads it. An old entry is moved there.
    • Config edits keep what is already there:
      • Comments in Zed and VS Code files, and the user's own keys in a Codex entry.
      • Each file's own line endings.
      • Files saved by Notepad or PowerShell (byte order mark, UTF-16) are read correctly.
      • integrate codex can no longer corrupt config.toml.
    • Claude Code capture hook: quoted correctly for Git Bash on Windows and non-ASCII user names. It also follows CLAUDE_CONFIG_DIR and autoMemoryDirectory.
    • MCP:
      • Every tool says it never reaches the internet.
      • Tool errors reach the model with their
  3. v4.11.04.11.0 - the menu bar opens at once, and one rule for what to store2026-09-30 · 2026-09-30 09:43 UTC
    查看更新说明

    The menu bar panel no longer opens your vault every time you click it, every agent now gets the same, narrower rule for what to store, and a race that could lose a memory while the vault was being opened is fixed.

    The menu bar

    Every click on the icon used to start two fresh Compartment programs, compartment status and then compartment recent. Each one loaded Python and decrypted the whole vault, thousands of starting memories included, to show a count and five lines. The second one then rewrote the vault file on its way out, which made every running agent's server read the whole vault again at its next memory call. On a test vault of 6,664 memories that took about 2.2 seconds before the panel could appear, and a read that failed or ran slow was shown as "locked".

    Now:

    • The panel opens at once from what it last knew, then refreshes in the background and updates in place when fresh numbers arrive. Measured on the same vault: 129 ms from the request to the panel on screen, with no vault read in between.
    • It refreshes only when something changed. It compares the modification times of the vault file, its settings, the stored unlock and the date, and when none of them moved it reads nothing.
    • When it does read, one helper process does the reading and keeps the vault open while you use the panel. Memories that agents add are read on their own, in about 20 ms, instead of decrypting the whole vault again. Only a vault file that was rewritten is read in full, about 0.6 seconds on the test vault.
    • Agent settings (Claude Code, Claude Desktop, Hermes, OpenClaw) are re-read only when one of those files changes.
    • Looking never changes the lock. The panel shows "locked" only when there is no stored unlock, or the stored one no longer opens the vault. A read that fails keeps the last known state and says what went wrong, and before the first read the panel says "checking" instead of guessing. The vault locks and unlocks only when something asks it to: the Lock and Unlock buttons, compartment lock and compartment unlock, an agent's memory_lock call, or a restart. Lock stays on the panel while the state is unknown, because locking is safe either way.
    • Looking never writes. The helper opens the vault read-only: no rewrite, no expiry sweep and no audit entry, the same rule the dashboard keeps.

    The helper talks to the panel over its own pipes, with no socket and no port. While it runs it holds the key, as each agent's MCP server does. It exits after ten idle minutes, lets go of the key within 30 seconds of a compartment lock typed in a terminal, and is stopped before the Lock button clears the stored unlock. On the test vault it used about 62 MB while open. SECURITY.md has the details.

    The tray on Windows and the panel window on Linux use the same code.

    What to store

    Every storage instruction Compartment gives agents now reads the same: the MCP handshake that Claude Code, Codex, Claude Desktop and other MCP clients receive, the memory_store tool, the CLAUDE.md block, the Hermes provider, the /compartmentalize skill, GEMINI.md and the Oh My Pi tool.

    STORE anything worth referencing again in future work: names, addresses, contacts, account IDs, passwords, API keys and other credentials, lasting file locations and configuration, preferences, and every durable fact or decision reached. Not transient chatter, one-off trivia, things freely available on the internet, or the working details of a task in progress, such as run results, errors, log contents and temporary paths or settings.

    The new part is the last clause: run results, errors, logs and temporary paths from work in progress stay out of long-term memory.

    A memory stored while the vault was opening could be lost

    Opening a vault reads the file, decrypts it, and then records the file's size and time as the version it holds. A memory another process appended in between was counted as read when it never was. The open's own tidy-up, which rewrites the file

  4. v4.10.14.10.1 - error corrections2026-09-22 · 2026-09-22 21:08 UTC
    查看更新说明

    Error corrections.

  5. v4.10.04.10.0 - a memory's age is how much of the vault is newer2026-09-16 · 2026-09-16 05:33 UTC
    查看更新说明

    Newer memories now outrank older ones, and a memory ages by how much of the vault has been written since it rather than by the calendar: two weeks old means almost nothing in a quiet vault and a great deal in a busy one.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Compartment

    Encrypted, fully offline memory for AI agents. One vault on your own computer, read and written by Claude Code, Claude Desktop, Hermes Agent, OpenClaw, Cursor, Codex and any other MCP client. No API key, no account, no network, no telemetry.

    PyPI Downloads CI License

    MCP Toplist Cursor Directory Glama MCP Market mcpservers.org LobeHub

    One-click install (after installing and running compartment init):

    Claude Code, Claude Desktop, Hermes Agent and OpenClaw are wired by one command instead: compartment integrate claude, hermes or openclaw.

    Compartment is persistent memory for AI agents, stored on your own computer. What an agent learns in one session is available in every later session, in every project, to every agent on the machine, and nothing leaves the machine.

    Each memory is a single claim, recorded with its source and the date it was learned. Memories can expire: set expires and the memory is removed after that date. When a preference changes, the new one replaces the old one. Recall is a hybrid vector and keyword search over an in-memory index. It answers in about 12 ms and returns only what is relevant.

    The embedding model is included in the package. Everything on disk is encrypted, including the embedding vectors, and only your passphrase opens it. A new vault comes with about 6,700 reference facts about hardware, operating systems, ports, encodings and shell tools. They are ordinary memories, and one switch removes them from search.

    How it compares with other memory servers

    Where each server keeps memory and what protects it, as documented by each project on 2 September 2026. Sources and the full table are in docs/COMPARISON.md; corrections are welcome as a PR against that file.

    Memory at restEncryptedAccount / API keyNetwork at runtime
    Compartmentone encrypted file; index in RAMyes, vectors toononenone, CI-enforced
    @modelcontextprotocol/server-memoryplaintext memory.jsonl, substring searchnononenone
    mem0 (open source)vector store + LLM-extracted facts; its MCP server is hosted onlynot documentedLLM keyLLM calls; telemetry on by default
    Graphiti (Zep) / LettaNeo4j / server + databasenot documentedLLM keyLLM calls; telemetry on by default
    claude-memlocal SQLite + Chromanot documentedsign-in requiredaccount + provider calls; telemetry on by default
    basic-memory (AGPL)Markdown + SQLitenot documentednonetelemetry on by default
    Hindsight (Vectorize)one container with embedded PostgreSQLnot documentedLLM key (local models configurable)LLM calls; vendor states no telemetry
    Supermemorycloud service, or a self-hosted prebuilt binarynot documentedaccount (cloud) or LLM key (self-host)cloud calls; self-host: vendor states no telemetry
    CogneeSQLite + LanceDB + Kuzu locally, or cloudnot documentedLLM keyLLM calls; telemetry on by default
    MemOSNeo4j + Qdrant self-hosted, or cloudnot documentedLLM keyLLM calls; telemetry on by default

    The memory logic

    Almost everything is stored. Only empty turns are dropped. A bare "OK" is a decision, not noise: when the agent asks "Want me to send this reply to the client now?" and the user answers "OK", Compartment stores the decision together with the question it answered. Small talk is kept but ranked last.

    Importance is assigned by fixed tiers. Decisions and consent 0.90, personal facts and preferences 0.80, the user's machine and configuration 0.75, other substantive statements 0.55, small talk 0.20. Importance multiplies a match score rather than adding to it, so it breaks near-ties in favour of what matters and can never surface a memory that did not match the question.

    One claim per memory, enforced. The store rejects anything longer than 200 characters (the max_memory_chars setting), and anything containing lists, headings or paragraphs, with an error that says how to split it. Instructions alone did not work: on a real vault, the median memory written by an agent was 1,938 characters of bulleted session log. memory_store_many stores a batch in one call. compartment atomize splits over-limit memories in an existing vault; each piece keeps the original's dates, and the original is marked superseded but stays readable by id.

    Every memory records its source and date. source is required: "from chat", "read from pyproject.toml", "web search". discovered is the date the fact was learned, separate from the date it was saved. Both are appended to the text as a short clause, for example [web search, 2026-08-01].

    Memories can expire. For a fact that stops being true on a known date, such as a sale price, a booking or a door code, set expires to that date (2026-09-03) or to a duration (14d, 2w, 3m, 1y). The memory is removed after that date. compartment expire runs the sweep by hand; expire_memories turns it off. Most facts should not expire; a wrong expiry deletes a memory the user wanted.

    Facts accumulate; opinions update. A new fact is added beside the others: the door code changed, a script lives at a path, a release shipped. An opinion replaces one. When a preference is stored with kind="opinion", the vault looks for a similar live opinion first. If it finds one, it returns the old record instead of inserting, and the caller resends with supersedes=[old id] to replace it, or supersedes=[] to keep both. Restating a live opinion refreshes its date instead of storing a copy. Superseded records are removed from search but kept in the audit chain and readable by id, with a pointer to their replacement. supersedes also works on facts, for corrections. Opinion ranking carries a recency bonus that fact ranking does not, so the newest opinion wins. compartment opinions audit finds overlapping live opinions in older vaults and keeps the newest, or reports them for manual merging.

    Capture does not depend on the model. A host that declares its own memory in its system prompt can override any tool instruction. So integrate claude installs a PostToolUse hook that writes each memory file Claude Code saves into the vault, whether or not the model calls the tool. The hook leaves your other hooks untouched, backs up settings.json first, always exits successfully so it can never break your editor, and does nothing while the vault is locked. compartment hook status | install | uninstall, or integrate claude --no-hooks. compartment import-claude imports anything the hook missed.

    Search returns what is relevant, not a fixed number. Compartment returns every memory whose score holds up against the best result for the same question, up to a generous cap. The cut is relative because scores are not comparable between questions: on a real vault, the nonsense query "how to bake sourdough bread" scored higher than the real query "what did Max decide about Airtable". A question the vault knows nothing about returns nothing. Pass top_k to get exactly that many.

    Tags are kept current. What a memory is about never changes; what it is relevant to does. Suppose that while working on a project called Northwind you learn that the client wants figures before conclusions. The agent tags the memory northwind. Two years later the same client, now called Harbour, hires you again, and the agent searches with the tag harbour. The memory is still true, but a tag filter cannot find it. So a background pass gives every memory the tags its nearest neighbours in embedding space were stored with, weighted by similarity: as Harbour memories accumulate near that old one, it picks up the harbour tag. Two more signals run alongside: tags that almost always occur together imply each other, and an existing tag whose phrase appears in a memory's text is attached. The pass writes only tags, never text, dates or embeddings. It only adds tags unless you pass --prune, tags_origin preserves the original tags, and compartment retag --dry-run shows what would change.

    A graph as well as a list. memory_link records a relation: subject, predicate, object, optionally tied to a memory and to a validity window. memory_relations answers by entity, by predicate, or as of a date. Compartment stores and matches relations deterministically; the host model decides what to link.

    Memories are data, not instructions. Recalled memories are wrapped with a notice that they are stored data. Content from an untrusted source can be marked quarantined, which adds a warning to every recall of it. The host agent must still treat memory as data.

    One embedding model per vault. The model's SHA-256 is recorded in the vault and checked on open, so similarity scores stay comparable. To change model, run compartment reindex --re-embed.

    No LLM inside. Embeddings run locally with a bundled 384-dimension int8 ONNX model, in one shared process of about 70 MB that every agent on the machine uses. The host model decides what to store and forget; Compartment captures, encrypts and recalls. That split keeps the offline guarantee absolute and every decision reproducible. With an offline LLM, the whole agent runs with no network.

    See what it learned. compartment recent lists the newest memories, hiding the reference facts so your own memories are visible. compartment status reports organic_records beside the total. memory_recent is the same view over MCP.

    The app and the dashboard

    The same panel on each system: the menu bar on macOS, the notification area on Windows, and an ordinary window on Linux, listed in the applications menu. Linux gets a window on purpose: a tray icon may never appear on GNOME or Wayland, and the control that unlocks your memories must not fail silently.

    The panel shows whether the vault is open, how many memories it holds and how many you stored, the three settings worth changing (capture hook, whether reference facts appear in search, auto-lock), which agents are connected with buttons to connect Claude, Hermes Agent or OpenClaw, and the last five memories. You can unlock, lock and change your passphrase there without a terminal. The panel opens instantly with what it last read and refreshes in the background, only when something actually changed. While you use it, one helper process keeps the vault open read-only and reads just the new memories as agents add them; after ten idle minutes it exits, so an idle app holds no vault and no key. Looking never locks, unlocks or rewrites the vault. It is meant to be one of the many apps on your computer, not something you have to learn: every function is a button or a switch, and the defaults were chosen by measurement.

    The Dashboard button opens the whole vault in your browser: growth over time, the relation graph with every entity named, tags, per-agent counts and live search. It is served from RAM on 127.0.0.1 only, read-only, with no outbound requests.

    The mathematics

    Everything below is in one file, src/compartment/ranking.py, used by the vault, the dashboard and the benchmark. A benchmark score therefore measures the product itself.

    Storage: long memories are embedded in windows

    The encoder reads 512 tokens. Text past that 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 exceeded the window and 57.6% of the text was invisible to semantic search.

    So each record is embedded as overlapping windows of W = 448 tokens with a stride of S = 384, giving 64 tokens of overlap so no fact is cut in half, 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, not average: a memory is relevant if any part of it is, and an average would penalise a long memory for its other parts. With one window per record this is identical to the old behaviour, so short memories are unaffected. Most memories are short: 6,705 records produced 6,785 windows. Windows are measured in model tokens, not characters, because a character budget is off by a factor of three between prose and a hex digest.

    Recall: two channels, combined as evidence

    Two indexes answer different questions. The vector index answers what a memory means; the keyword index answers what it says. Their scores are not on the same scale, and combining them is the whole problem.

    Until 4.7, Compartment added them. Adding lets a merely-good semantic match outvote conclusive literal evidence: searching a real vault for a commit SHA that appears in exactly one memory returned that memory below ten paraphrases of it, because the sum buried a first-place keyword hit.

    The two channels are alternatives, not addends: 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 but keeps spreading results 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 near certainty carries the memory alone; neither can veto the other.

    Turning a cosine into a probability. An L2-normalized encoder gives cosines that are comparable across queries, so fixed bounds map them. Per-query min-max normalization would rescale the best hit of a hopeless query up to 1.0 and throw that information away.

    p_vec(d) = clamp( (cos(q, d) - 0.25) / (0.85 - 0.25),  0,  0.88 )
    

    The 0.88 ceiling matters. A cosine is a similarity, never an identity: an encoder can say this is about the same thing, never this is the record you named. A literal match on a string unique to one memory can. So the semantic channel is capped below what the literal channel can reach, and the cap is forced by the weights: the literal channel tops out at 0.25 · -log(1 - 0.999) = 1.727, so 0.75 · -log(1 - cap) < 1.727, giving cap < 0.90.

    Turning a keyword hit into a probability, without BM25. BM25 measures how well a document matches, which does not settle a contest against a semantic hit. What settles it is how unlikely the match was by chance. Each query term carries its self-information over the vault, and a memory scores the fraction 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; a term in a tenth of the vault is almost nothing, whatever its BM25. This is what makes literal and semantic hits comparable.

    The keyword index is queried as AND first, because an exact phrase is the strongest signal. FTS5's implicit AND requires a nine-word question to appear word for word, so when AND finds nothing it falls back to OR over the informative terms only: anything in more than 10% of records is dropped. That threshold is measured from the vault, not taken from an English stopword list, so it works the same for code, names or other languages.

    A small rank-agreement term is added, the one thing reciprocal-rank fusion does well, sized to break ties:

    + w_rrf · k · [ 1/(k + rank_vec) + 1/(k + rank_lex) ]      w_rrf = 0.10, k = 20
    

    Importance and recency reorder the score

    evidence(d) = aged( vec term, q(d) ) + lex term + rank residue
    final(d)    = evidence(d) · ( 1 + w_imp · (2·importance(d) - 1) + w_op )
    
    aged(s, q)  = log( 1 + 2^( -q / half_life_share ) · (e^s - 1) )
    
    q(d)             = share of the vault's own memories written after this one
    half_life_share  = 0.5, so the median memory is worth half the odds
    w_imp            = 0.15, for facts and opinions alike
    w_op             = 0.30 · 2^( -age_days / 30 ) for an opinion, from the last
                       re-affirmation (`affirmed`); 0 for a fact
    

    Multiplicative, so a prior can only reorder memories that already matched. An additive prior would let an important memory surface for an unrelated question. A memory that matched nothing scores zero and stays there.

    Centred on the 0.5 default, hence 2·importance - 1. Every unweighted memory carries 0.5, including the thousands of reference facts, so without centring they would all get the same boost and importance would do nothing. Centred, an unweighted memory is neutral and only a deliberate weight moves it.

    A memory's age is counted in memories, not in days. A fact does not get less true in six months; what makes an old memory the wrong answer is that the vault has moved on, and how far it has moved is a question about how much was written. So a memory is as old as the share of the vault written after it. Two memories added in a fortnight leaves a fortnight-old memory untouched; five hundred added in the same fortnight halves the odds behind its semantic evidence. The population counted is the vault's own live memories in the namespaces being searched: not the reference facts and not an installed pack, which arrive in their thousands at one instant and have no age in this sense.

    The shift is applied to the semantic channel only, and in odds, so a strong match is nudged and a faint one is scaled away. A literal identifier is left alone: a commit SHA that appears in exactly one memory names that memory whether it was written yesterday or last year. And the relevance floors read the unaged score, so recency chooses the ORDER of the results and never which ones come back. One consequence: a returned memory's reported score is the aged one and can sit below the absolute floor it passed, so the floor is a cut the vault makes and not a property of the number it hands back.

    An opinion carries a second prior on top of that, and that one is on the clock: it halves every 30 days from when it was last re-affirmed, heavily enough that the newest opinion on a subject wins.

    Retrieval order

    Namespace, tag, date and reference-fact filters run after ranking, so a pool sized to the requested number of results could 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. Below 20,000 records the vector search is exact (SIMD matrix math, recall 1.0); above that, HNSW at about 99% recall, unless usearch can use no SIMD on the machine - an agent's sandbox can hide the CPU features it probes - in which case the exact index stays, because rebuilding HNSW without SIMD on every open cost from 50 seconds at 25,000 records.

    Security and the lock model

    The primitives: XChaCha20-Poly1305 encryption on everything at rest, including embedding vectors, because vectors can be inverted back to text · Argon2id key slots, LUKS-style · a key per record, so forget --shred destroys the key and the content is unrecoverable rather than marked deleted · an fsync'd sealed journal, atomic compaction, and tested kill -9 recovery · a hash-chained audit log (compartment audit verify) · signed vault manifests and packs · stdio transport with no open ports; the one local socket is the shared embedding process's Unix socket, in the same private directory as the unlock credential, carrying text in and vectors out and never a key · a runtime guard that aborts on any network socket attempt (--assert-offline), with CI running the whole suite under it on Linux, macOS and Windows. The full threat model, including what Compartment cannot protect against, is in SECURITY.md.

    From the app

    Everything you do day to day is a button. Unlock asks for your passphrase; Lock closes the vault and clears every stored credential; Change password rekeys it; Auto-lock chooses 15, 30 or 60 idle minutes, or never. Compartment never generates a password, seed or recovery phrase, and holds no credential you do not.

    After an unlock the vault stays open across processes, logouts and logins for as long as you leave it, until a restart or power loss, until the auto-lock timer fires, or until you lock it. A restart or power loss always locks it: the unlock credential is the master key wrapped with a random per-boot secret that lives only in kernel memory and is never written to disk, so a new boot cannot open it. A copy of the credential file on its own is useless.

    From the command line

    The same controls, plus two that only exist here:

    • compartment unlock and compartment lock do what the buttons do. Agents can lock with the memory_lock tool. (Vaults from older versions that were issued a recovery phrase still accept it.)
    • compartment 2fa enable adds a second factor: your passphrase plus a keyfile, for example on a USB stick. Both feed Argon2id together, so the requirement is enforced by the cryptography, not by a setting; a stolen vault file plus your passphrase opens nothing without the keyfile. The keyfile's location is remembered, so unlocking feels the same while it is present.
    • compartment unlock --keychain on macOS is an explicit opt-in that survives reboots.

    The memory_unlock MCP tool exists but is off by default, because enabling it puts the passphrase in the model's context.

    One vault, many agents, any machine

    Without the command line

    Every agent on the machine uses the same vault, and none of that needs setting up: the app's Connect an agent buttons wire Claude, Hermes Agent and OpenClaw, and what one agent stores the others recall. Claude, Hermes Agent, Cursor and the CLI can use the vault at the same time: writes are serialised by a file lock, every process notices writes by others and reloads, and each agent has its own identity and namespace. Their servers share one embedding process too, so ten agents cost one model in RAM, and it leaves a few minutes after the last of them does.

    A locked vault is one file, memory.vault in the .compartment folder of your home directory. To move to another machine, lock the vault, copy the file there, install Compartment and unlock it in the app with your passphrase.

    From the command line

    The same move, signed so the recipient can check it, plus the escape hatches:

    compartment lock --sign
    scp ~/.compartment/memory.vault other-machine:
    compartment --vault memory.vault unlock     # your passphrase (+ keyfile if 2FA)
    

    lock --sign adds an Ed25519 manifest that the recipient can check with compartment verify and no credential. export --plaintext writes the vault as JSONL and import reads it back, so you are never locked in. FORMAT.md specifies the .vault and .mpack files byte by byte. Per-agent namespaces take rw, ro or none grants in the settings file, so a scratch agent can read without writing.

    Memory packs are signed, read-only bundles of curated memories (compartment pack build | install | remove | list | export). They install under packs/<name>, read-only for every caller, and include_packs_in_search toggles them. A pack's signature is checked against a key you trust, never against the key inside the pack. The reference facts are the one pack that lives in main as ordinary memories. PACKS.md covers authoring.

    compartment setup airgap-bundle prepares an install for a machine with no network; setup download-model and setup download-longmemeval fetch what the optional benchmarks need.

    Measured, on an 8 GB baseline laptop

    Every number below is reproducible on your machine with compartment selftest and compartment bench (--longmemeval runs the retrieval benchmark); compartment embed-daemon status reports the shared process's own size.

    MetricMeasured
    Fresh install → open vault, offlineseconds, zero network
    Vector search, 20k records (HNSW)p95 0.68 ms
    Full hybrid search (embed + windows + keywords + evidence fusion)median 11.6 ms, p95 14.7 ms
    Shared embedding process, model loaded, once per machine68 MB resident
    The same after two batches of 64 long text windows71 MB; before 4.9.6 every agent's server kept 1.5 GB, then 3 GB
    An MCP server process with its model in the shared daemon60 MB, plus its vault and index
    Store one memory (embed + encrypt + fsync journal)~40 ms
    Wheel size, model included~30 MB
    Test suite (crypto, tamper, crash, offline, concurrency, 2FA, graph, dash, ranking)800+ tests, offline guard active

    Install

    No command line needed. On a Mac, download Compartment-<version>.pkg (Apple silicon) or Compartment-<version>-intel.pkg (Intel) from the latest release and open it. Python, the embedding model and every dependency are inside it. It asks you to choose a passphrase, creates the vault, and puts Compartment in your menu bar, where the Connect an agent buttons do the rest.

    From the command line, on any system:

    pipx (recommended)pipx install compartment && compartment init
    uvuv tool install compartment && compartment init
    pippip install compartment && compartment init inside a virtual environment. Debian 12+, Ubuntu 23.04+, Fedora, Arch and Homebrew refuse a system-wide pip install with "externally-managed-environment"; pipx and uv avoid that. On Windows, py -m pip install compartment
    Install scriptfrom a checkout: bash install/install-linux.sh (Linux, WSL, ChromeOS), bash install/install-macos.sh, or powershell -ExecutionPolicy Bypass -File install\install-windows.ps1. Each picks a Python that works on the machine, handles the externally-managed case, creates the vault and connects the agents it finds
    Dockerdocker build -t compartment . from a checkout; see Wiring each agent

    Agents can also install it from their own stores - the Claude Desktop extension (compartment-<version>.mcpb on each release), the Claude Code and Codex plugin marketplaces, Gemini CLI extensions, Hermes Agent plugins and more. Each still needs the vault from compartment init, because the passphrase is typed by you. docs/PLUGINS.md lists every store and its install command.

    Where it runs

    SystemCPUPython for pip / pipx / uvThe app
    macOS 14 or newerApple silicon3.11 - 3.14menu bar; .pkg / .dmg
    macOS 13.4 or newer, and every Intel MacApple silicon, Intel3.11 - 3.13menu bar; -intel.pkg / -intel.dmg on Intel
    Windows 10 / 11x643.11 - 3.14tray icon
    Windows 11 on ARMARM643.12 - 3.14tray icon
    Linux with glibc 2.28+ (Ubuntu 20.04+, Debian 11+, Fedora, RHEL 8+, Arch, openSUSE), WSL 2, ChromeOS Linuxx86_64, aarch64 (incl. Raspberry Pi 5)3.11 - 3.14a window (needs Tk: python3-tk or your distro's equivalent)
    Dockeramd64, arm64in the imageheadless

    The limits come from onnxruntime, the one dependency without wheels everywhere: its macOS builds need macOS 14 from 1.24 on, and its last Intel Mac build (1.23.2) stops at Python 3.13. CI runs the suite on every row and checks the whole matrix for a dependency that would have to be compiled (tools/wheel_coverage.py). On Windows, onnxruntime also needs the Microsoft Visual C++ runtime (ARM64); Compartment says so if it is missing.

    Elsewhere, with the system's onnxruntime. Alpine (musl), FreeBSD and Android (Termux) package onnxruntime themselves; install it from the system and give Compartment a virtual environment that can see it. The install script does this for you.

    # Alpine
    apk add python3 py3-onnxruntime
    # FreeBSD (default Python 3.12; the compiled dependencies come from packages)
    pkg install py312-onnxruntime py312-tokenizers py312-pydantic-core py312-pynacl py312-argon2-cffi py312-numpy py312-cryptography
    # Termux
    pkg install python onnxruntime python-numpy rust
    
    # then, on any of them
    python3 -m venv --system-site-packages ~/.compartment-venv
    ~/.compartment-venv/bin/pip install compartment && ~/.compartment-venv/bin/compartment init
    

    On Termux there is no shared memory for the unlock-until-restart credential, so set COMPARTMENT_PASSPHRASE or use compartment unlock --once. A glibc older than 2.28, 32-bit systems, armv7 and riscv64 are not supported. On WSL 2, the VM shutting down after it has been idle clears the unlock credential, so the vault relocks between sessions.

    init asks you to choose a passphrase, creates the vault, loads the reference facts, connects Claude Code, Hermes Agent or OpenClaw if they are installed, and starts the app: a menu bar item on macOS, a tray icon on Windows, a window on Linux. Restart your agent and it has a memory.

    To connect an agent later, or any other client:

    compartment integrate claude      # Claude Code + Claude Desktop
    compartment integrate hermes      # Hermes Agent
    compartment integrate openclaw    # OpenClaw
    compartment integrate --list      # the 33 MCP clients it can wire: Cursor, VS Code, Windsurf, Cline, Roo Code, Kilo Code, Zed, OpenCode, Codex CLI, Gemini CLI, Kiro, Amazon Q, Junie, Goose, LM Studio, Continue ...
    compartment integrate --all       # every one of them that is installed here
    

    claude, hermes and openclaw also get the /compartmentalize skill installed in their skills directories. Any other MCP client uses this block (stdio transport, no environment variables):

    { "mcpServers": { "compartment": { "command": "compartment", "args": ["serve"] } } }
    

    An app started from the Dock, Finder or the Start menu does not see your shell's PATH, so a bare compartment can fail there with "spawn compartment ENOENT". Use the full path that which compartment (where compartment on Windows) prints; integrate always writes that full path.

    Wiring each agent

    None of this needs a terminal: the Connect an agent buttons in the app run the same steps for Claude, Hermes Agent and OpenClaw. The commands below are for people who prefer them, and for wiring a client the app does not list. On Windows, run them in PowerShell with py -m pip install compartment in place of pip install compartment.

    Claude (Code + Desktop)

    pip install compartment && compartment init && compartment integrate claude
    

    Registers the MCP server with the Claude Code CLI (user scope, all projects), imports the memories Claude Code has already written to its memory files (copy-only and repeatable; --no-import skips it), installs the capture hook (--no-hooks skips it), installs the /compartmentalize skill, writes a managed block into CLAUDE.md, and, when Claude Desktop is installed, registers the server in Claude Desktop's config file, backed up first. The server also describes itself in the MCP handshake, telling the model to recall before answering and to store durable facts, credentials, names and decisions, so Claude uses Compartment as its memory without any manual instruction.

    Hermes Agent

    pip install compartment && compartment init && compartment integrate hermes
    

    Installs the provider plugin into the Hermes environment and runs hermes memory setup compartment; verify with hermes memory status. Hermes Agent 0.20.0 and newer also read the portable Agent Plugins format, and this repository is one. That route installs the MCP server and the /compartmentalize skill from GitHub:

    pip install compartment && compartment init
    hermes plugins install MaxFreedomPollard/Compartment
    hermes plugins enable compartment
    

    The provider is the fuller integration, because it recalls and stores on every turn; the portable package works only when the model calls its tools. On macOS and Windows both install to the same plugin directory name, so use one or the other.

    OpenClaw

    pip install compartment && compartment init && compartment integrate openclaw
    

    Writes the mcpServers entry into ~/.openclaw/openclaw.json, with a backup. Then run openclaw gateway restart and check with openclaw mcp list.

    Any MCP client

    compartment integrate <client> wires any of the 33 clients in --list. Each config write takes a byte-exact backup first, merges rather than replaces, writes atomically, and refuses to touch a file it cannot parse (it prints the block to paste instead). To do it by hand, use the block in Install; VS Code uses the key servers with "type": "stdio", Zed uses context_servers, Codex uses TOML under [mcp_servers.compartment]. --vault and --caller are optional; the defaults are ~/.compartment/memory.vault and caller user. Client-by-client walkthroughs are in docs/INTEGRATIONS.md.

    Docker

    docker build -t compartment . from a checkout builds a headless image (amd64 and arm64): stdio only, no port, unprivileged user, the offline guard on, vault on a bind mount at /data. Create the vault on the host first with compartment init, because that step prompts for the passphrase, then give the client this command:

    docker run -i --rm --network none --user "$(id -u):$(id -g)" \
      -v "$HOME/.compartment:/data" -e COMPARTMENT_PASSPHRASE compartment
    

    --user lets the container read your 0600 vault on a Linux host; Docker Desktop on macOS and Windows does not need it.

    Configuration

    Nothing here is required. Compartment installs configured; this is the whole surface if you want to change something.

    In the app

    The panel behind the icon: Unlock and Lock, Change password, Create memories automatically (the capture hook), Search starter facts, Auto-lock (15, 30, 60 minutes or never), the CONNECT AN AGENT buttons for Claude, Hermes Agent and OpenClaw, Refresh and Quit.

    Commands

    Global flags, before the command: --vault PATH, --caller NAME, --keyfile PATH, --assert-offline, --version.

    CommandWhat it does
    initcreate the vault. --passphrase, --creator, --keychain, --no-session, --no-app
    unlock / lockopen or close it. --passphrase-stdin, --keyfile, --keychain, --once; lock --sign --identity
    status / verify / selftestwhat is in it, is it intact, does it work
    store / get / forgetone memory. --source (required), --discovered, --expires, --namespace, --tag, --importance, --kind fact|opinion, --supersedes ID, --keep-both, --quarantined, --raw; forget --shred
    search / recentfind things. --namespace, --tag, --top-k, --limit, --all, --json
    expireremove expired memories
    atomizelist over-limit blob memories as JSONL (--out + --plaintext), apply an agent-written split plan (--apply)
    opinions auditbackfill kind on opinion-shaped records, cluster overlapping live opinions, resolve with --keep-newest. --threshold, --no-backfill, --json
    link / relations / unlinkthe relation graph, with validity windows (--from, --to, --as-of)
    panel (menubar, tray)the app. --show, --self-check, --render, --login
    integrate <agent>wire claude, hermes, openclaw or any listed client, and install /compartmentalize. --list, --all, --no-import, --no-hooks; integrate --refresh updates the instructions an earlier install wrote into agent files and nothing else
    hookthe Claude Code capture hook: install --pin-vault, uninstall, status, capture
    import-claudepull in what Claude Code already wrote. --dir, --namespace, --dry-run
    servethe MCP server, over stdio
    embed-daemonthe shared embedding process every agent uses: status, stop, run
    dashread the vault in a browser: 127.0.0.1, one-time token, GET only
    export / importexport --plaintext writes it unencrypted; import reads it back
    rekeychange the passphrase. --new-passphrase-stdin
    2faenable, disable, status: a keyfile as a second factor
    auditverify, repair the hash-chained history
    retagrecompute tags from the current vault (--dry-run, --prune); never changes memory text
    reindexrebuild the index, and give long records the embedding windows they are missing. --int8, --f32, --re-embed, --model
    packbuild, install, remove, list, export signed memory packs (--trusted-key)
    bench--records, --longmemeval, --variant, --limit
    setupdownload-model, download-longmemeval, airgap-bundle
    updateupgrade in place, then refresh the instructions in agent files. --source takes GitHub main, --no-app skips the restart
    uninstallremove it. The vault is kept unless you pass --purge

    compartment panel --login on | off | status controls starting at login (on Linux, the applications menu entry). init --no-app skips the app on headless machines and in CI.

    compartment dash is the Dashboard button from the terminal: the whole vault in your browser, growth over time, the relation graph with every entity named, tags, per-agent counts, live search. It serves from RAM on 127.0.0.1 only, behind a random URL token, read-only, with no outbound requests and no configuration. Ctrl-C closes it.

    The /compartmentalize skill

    compartment integrate <agent> writes one file into that agent's own skills directory, and compartment uninstall takes it back:

    AgentPath
    Claude Code~/.claude/skills/compartmentalize/SKILL.md
    Hermes Agent$HERMES_HOME or ~/.hermes/skills/compartmentalize/SKILL.md
    OpenClaw$OPENCLAW_HOME or ~/.openclaw/skills/compartmentalize/SKILL.md

    All three use the same Agent Skills layout, so it is one file. Only the user runs it. Type it before compacting, or at any time, and the agent stores the whole conversation in the vault: people and contacts, credentials and where they live, URLs and hosts, decisions and the reasons for them, and a record of the session itself. It makes many memory_store calls. You can edit your copy; a later install backs up a changed copy rather than overwriting it.

    Settings file

    <vault>.config.json, beside the vault, holding grants per caller and:

    SettingDefaultMeaning
    auto_lock_minutes30idle time before it locks. 0 never locks
    search_starter_factstruewhether the seeded facts join search results
    include_packs_in_searchtruethe same, for installed packs
    recency_half_life_share0.5how much of the vault has to be newer than a memory before its semantic evidence is worth half the odds. 0 turns the recency prior off
    expire_memoriestrueremove expired memories automatically
    duplicate_threshold0.97cosine similarity at which a store is a duplicate
    max_memory_chars200the one-claim length limit for authored memories. 0 disables the length and layout checks
    opinion_update_threshold0.80similarity at which a new opinion is an update of a live one and needs a supersedes decision
    opinion_reaffirm_threshold0.97similarity at which a restated opinion re-affirms the live record instead of storing
    retag_interval_hours6how often the background pass recomputes tags. 0 turns it off
    retag_prunefalsewhether that pass may also remove tags
    index_precision"f32""int8" uses a quarter of the RAM
    embed_daemontrueask the machine's shared embedding process for vectors instead of loading the model in this process
    unlock_tool_enabledfalselets an agent unlock the vault. Off because the passphrase would cross the model's context

    Environment

    COMPARTMENT_VAULT which vault to use, COMPARTMENT_PASSPHRASE for scripts and CI, COMPARTMENT_SESSION_DIR where the unlock credential lives, COMPARTMENT_UI_SCALE panel scale, COMPARTMENT_ASSERT_OFFLINE abort on any network attempt. COMPARTMENT_EMBED_DAEMON=0 keeps the embedding model inside each process instead of the shared one, COMPARTMENT_EMBED_SOCKET moves that process's socket, COMPARTMENT_EMBED_IDLE is how many seconds it outlives its last client (300). HERMES_HOME, OPENCLAW_HOME and XDG_DATA_HOME are read where they apply. Anything exported as ENGRAM_* still works.

    MCP tools

    Every tool has a title and a read-only or destructive annotation, so a client can tell the seven read-only tools from the ones that write before calling anything. memory_search, memory_store, memory_store_many, memory_get, memory_recent, memory_forget, memory_link, memory_relations, memory_unlink, memory_list_namespaces, memory_status, memory_lock, memory_selftest. memory_unlock exists but is off unless you turn it on above.

    Documentation

    docs/MEMORY.mdhow memory is stored, what gets remembered, and the ranking design
    docs/PLUGINS.mdevery plugin store and listing: how users install from it, what the maintainer does
    docs/INTEGRATIONS.mdselecting Compartment in Hermes Agent, OpenClaw, Claude, everything else
    docs/COMPARISON.mdother memory servers, with sources
    SECURITY.mdthe full threat model and its limits
    FORMAT.mdbyte-level .vault and .mpack specs (language-agnostic)
    PACKS.mdauthoring and shipping signed memory packs
    CONTRIBUTING.mdsetup, good issues, and the guarantees to keep
    RELEASING.mdhow a release is cut

    Privacy Policy

    Compartment collects no data: no telemetry, no analytics, no account, and no network at runtime. Memories are stored only on your machine, AEAD-encrypted at rest with a passphrase that never leaves it, and nothing is shared with anyone. The full policy, covering collection, storage, network access, third-party sharing, retention, and contact, is at https://maxfreedompollard.github.io/Compartment/privacy.

    Where to find it

    Compartment is listed on PyPI, the official MCP registry, the Cursor Directory, Glama, LobeHub, MCP Toplist, MCP Market, mcpservers.org, TensorBlock, the toolsdk.ai registry, Libraries.io, Snyk Advisor and deps.dev, and in the curated lists abordage/awesome-mcp, TensorBlock/awesome-mcp-servers and Jenqyang/Awesome-AI-Agents.

    MCP Toplist LobeHub

    Bugs and feature requests: Issues. Support and questions: Discussions; security reports: SECURITY.md. Questions and ideas: Discussions.


    mcp-name: io.github.MaxFreedomPollard/compartment