Capabilities · 1. Memory that persists
Every agent you run forgets everything the moment it stops, and a team running several of them cannot see what any of them believe. This family is the daemon's answer: a durable, human-readable record of short statements your agents write and read back, with privacy, tenancy and deletion built around it rather than bolted on.
Six capabilities. The first is the one everything else in the product stands on; read it even if you skip the rest.
| § | Capability | Status | For |
|---|---|---|---|
| 1.1 | Durable fact memory | SHIPPED | both |
| 1.2 | Private by default | SHIPPED | both |
| 1.3 | Tenant partitioning | SHIPPED, with a caveat you must act on | human |
| 1.4 | Erasure: deletion, compaction, retention and legal holds | SHIPPED; legal holds default off | human |
| 1.5 | The memory panel | FLAG, default on | both |
| 1.6 | Assisted memory formation | FLAG, both parts default off | both |
The shape of the family is worth stating before the detail. Writing is deliberate: an agent decides a thing is worth remembering and says so. Nothing is captured automatically in the default configuration, and the one on-ramp that extracts candidates for you is default off and files them for human review rather than storing them (§1.6). Reading is scoped: what comes back depends on who is asking and which tenant they are in. And deleting is a two-step act, because the thing that makes memory durable is the same thing that makes it stubborn (§1.4).
1.1 Durable fact memory
Status · SHIPPED Reached through · MCP
store_fact,query_facts,delete_fact,list_entities,fact_history· HTTPPUT /v1/facts,GET /v1/facts,POST /v1/facts/aggregate· Console → Memory › Facts Who it is for · both
What it does. An agent writes a short statement with three parts, a subject, an attribute and a value, and reads it back on its next run, after a restart, after a reboot. Writing the same subject and attribute again does not overwrite the old value: it mints a new version and retires the previous one, so the chain of what was believed and when stays readable. Around that sit recall by subject, by prefix and by substring, a list of every subject in the store, the version history of any one statement, and deterministic aggregates that give reproducible counts over a result set rather than a sampled estimate.
Why it works this way. The store is one line of JSON per fact, appended to a file you can open in a text editor. That is a deliberate rejection of the obvious alternative, an embedded database with in-place updates and an index on disk. The reasoning is that the thing you most want from a memory that outlives a crash is the ability to open it and see what happened, without needing the process that wrote it to still be running or still be healthy. A lab notebook, not a table. The trade-offs accepted in exchange are real and are stated below: weaker durability than a database, no query language, and a boot cost and memory footprint that both grow with the size of the journal. Versioning rather than overwriting comes from the same instinct. An agent that corrects itself is common; an agent that silently destroys the reasoning it corrected is a debugging problem you cannot recover from.
What changes for you.
- As an operator: you can read your fleet's memory with
cat. When recall looks wrong, the raw record is a text file, not an opaque index, and "what did this agent believe last Tuesday" is answerable from the version chain rather than from a backup. - As an agent: a decision recorded once is findable next session, and correcting it later does not destroy the earlier answer. Writing under disciplined subject names, one naming scheme per kind of thing, is what makes recall predictable, because recall matches on the text you wrote.
What it does not do.
- It does not fsync on the default write path. A write that returned success can still be lost to a power failure. If you need a durability guarantee, take a filesystem snapshot; do not infer one from a 200.
- It does not search by meaning. Recall over facts is substring matching over the text you stored, which is why chapter 3 opens with that fact rather than burying it.
- It does not remove content when you delete a fact. Deletion is a marker; removing the value from disk is compaction, and §1.4 is about the difference.
- It does not bound its own growth. Boot time and steady-state memory both scale with the journal, and compaction is the only thing that shrinks it.
Where the detail lives. The record and every one of its fields is daemon/10 §10.2; the routes and their shapes are api/01 §1.1; the type itself is fact_store.rs:169.
1.2 Private by default
Status · SHIPPED Reached through · MCP
store_factwithprivate: true, and enforced on every MCP recall surface · Console → Memory › Facts Who it is for · both
What it does. A long list of reserved subject prefixes is forced private at write time, whatever the caller asked for, and rewritten into a per-agent namespace. An agent's own working memory, its operational notes, its coordination intents and its decision records are therefore invisible to a sibling agent and are excluded from sync, from export and from the console's fact list without anyone having remembered to mark them.
Why it works this way. The rejected alternative is opt-in privacy, where an agent marks sensitive writes as it goes. That fails the way every opt-in control fails: the one write that mattered is the one nobody marked. Making the daemon's own reserved namespaces born private moves the default to the safe side, and leaves ordinary user facts shared by default because that is what a team store is for. The trade-off accepted is that the policy is fixed for the life of the process; it is read once at start-up and cannot be changed while the daemon is running, so changing it is a restart, not a toggle.
What changes for you.
- As an operator: you can run several agents against one daemon without their scratch memory bleeding into each other's recall, and without auditing every write path first. Extending or narrowing the reserved list is a start-up decision.
- As an agent: anything you write under your own namespace is yours. With no agent token configured, which is the default local posture, a caller sees no private facts at all, including its own from a previous identity.
What it does not do.
- It is not encryption. A private fact is plain text in a file, and anyone who can read the data directory can read it.
- It is not a store-level access control list. Privacy is enforced by filters at the call layer, on the MCP recall surfaces, on sync push, on export and in the console, and the store's own internal query path applies no privacy filter at all. That is why those internal accessors are marked administrator-only.
- It does not do group sharing. Visibility is owning-identity-only, and sharing a private namespace with a named group is explicitly not implemented.
Where the detail lives. Every enforcement point, and the ones that deliberately do not enforce, are tabulated in daemon/10 §10.7; the reserved list is fact_privacy.rs:91.
1.3 Tenant partitioning
Status · SHIPPED, with a caveat you must act on Reached through · every fact route and MCP fact tool · Console → Memory › Tenants Who it is for · human
What it does. Every fact carries a tenant marker, and the daemon sets it from the authenticated context before storage, so a caller cannot write a fact into somebody else's tenant by putting a different value in the request body. A tenant-scoped read path exists and returns only that tenant's facts.
Why it works this way. The write side is closed because that is where forgery would be cheap and undetectable. The read side was left opt-in for a blunt reason: the store predates multi-tenancy, and defaulting an unscoped query to "everything" preserved the behaviour of every caller written before tenants existed. That is a compatibility decision, not a security design, and it is the caveat below.
What changes for you.
- As an operator: you can hold several clients or several teams in one daemon and see them separated in the console, provided the callers you deploy use the tenant-scoped read path.
- As an agent: your writes land in your own tenant automatically. You do not have to, and cannot usefully, set the tenant yourself.
What it does not do.
- It does not filter by tenant unless the caller asks. A query with no tenant specified returns every tenant's facts. Isolation in the fact store depends on callers choosing the scoped variant, so treat "one daemon per client" as the strong boundary and tenant scoping as the convenience one.
- It is not the same mechanism as the document lane's tenancy, which is a stronger, always-checked hash comparison. Do not reason from one to the other.
Where the detail lives. The scoped and unscoped read paths are set out in daemon/10 §10.8; how a request acquires its tenant is daemon/07 §7.8; the opt-in filter itself is fact_store.rs:409.
1.4 Erasure: deletion, compaction, retention and legal holds
Status · SHIPPED. Legal holds behind
CORECRUXD_FEATURE_LEGAL_HOLD, default off Reached through · HTTPPOST /v1/admin/actionswithcompact-facts,POST /v1/legal-holds,DELETE /v1/legal-holds/{id}· CLIcorecruxctlWho it is for · human
What it does. Deleting a fact marks it deleted; it stops appearing in recall immediately. Removing its content from disk is a second, explicit act: compaction rewrites the journal, keeping live facts and writing value-free markers where deleted ones were. A retention sweep automates the first step by age, and a legal hold, behind CORECRUXD_FEATURE_LEGAL_HOLD which is default off, blocks compaction outright until an override receipt is produced that enumerates every held fact.
Why it works this way. An append-only journal cannot delete in place without becoming something else, so erasure had to be a compaction pass. Making it explicit rather than automatic is the decision worth understanding: compaction is the only operation in the memory substrate that destroys data, and an operator should be the one who runs it, at a time of their choosing, having read what it will remove. The legal hold exists because the two obligations, delete on request and preserve for litigation, arrive at the same store from different directions, and the daemon refuses to guess which one wins. It refuses to compact and makes you produce a record of the override instead.
What changes for you.
- As an operator: this is the mechanism behind a retention policy and a data-subject erasure request. The honest sequence is mark, verify, then compact, and it is worth rehearsing before you need it. Compaction also has a side effect worth knowing: because it re-serialises what is currently in memory, some deliberately ephemeral state becomes durable as a consequence of maintenance.
- As an agent: a delete you issue is immediately invisible to recall but is not gone. Do not describe it to a user as erasure until an operator has compacted.
What it does not do.
- Deleting does not remove the value from disk. Until compaction runs, the value is still in the journal file.
- Compaction does not remove version history. Superseded versions are distinct live facts and survive. "Nothing is ever destroyed" is true of consolidation and false of the store as a whole; both halves of that sentence matter.
- The retention sweep does not touch private facts. They are skipped by design, so a retention policy that assumes it caught everything has not.
- It is not crypto-shred. There is no key-destruction path in this edition, and chapter 9 says so plainly.
Turn it on. Legal holds require CORECRUXD_FEATURE_LEGAL_HOLD, default off. Compaction and the retention sweep need no flag.
Where the detail lives. What compaction keeps and what it destroys is daemon/10 §10.6; the hold routes and the override receipt are api/04 §4.7; the retention sweep is fact_store.rs:1951.
1.5 The memory panel
Status · FLAG
CORECRUXD_FEATURE_MEMORY_PANEL, default on. Setting it to0disables it Reached through · MCPmemory_view,memory_edit,memory_pin,memory_history· Console → Memory › Memory Who it is for · both
What it does. Behind CORECRUXD_FEATURE_MEMORY_PANEL, which is default on, the panel is the human-shaped view of the store: browse the recent window under a token budget, correct a value, pin a fact so consolidation cannot touch it, and read the history of any one statement. It is the same store the agents use, not a copy.
Why it works this way. A memory nobody can look at is a memory nobody trusts. The panel exists because the raw journal is readable but not browsable, and because the operator needs three verbs the agents do not: correct, pin, and inspect history. Pinning in particular is the manual override on the automatic machinery in chapter 2; it is how a person says "this one is right, leave it alone" to a consolidation pass that would otherwise be free to collapse it.
What changes for you.
- As an operator: when an agent has learned something wrong, you fix it in place rather than instructing an agent to overwrite it, and you can see the correction in the version chain afterwards.
- As an agent: the same window is available as a tool, budgeted, so an agent can review recent memory without pulling the whole store into context.
What it does not do.
- It does not show private facts belonging to other identities. The panel is subject to the same visibility filters as recall.
- It is not a query interface. It shows a recent window, not a search over everything; searching is chapter 3.
- Editing does not rewrite history. A correction is a new version, which is the point.
Turn it on. On by default. Set CORECRUXD_FEATURE_MEMORY_PANEL=0 to remove the tools.
Where the detail lives. The tools and their arguments are api/12 §12.10; the console destination is operations/04 §4.2; the handler is memory.rs:155.
1.6 Assisted memory formation
Status · FLAG. Engram tools behind
CORECRUXD_FEATURE_ENGRAM_MCP, default off; auto-capture behindCORECRUXD_AUTO_CAPTURE, default off Reached through · MCPengram_resolve· HTTPGET /v1/engrams,POST /v1/memory/session-init,POST /v1/memory/extract,GET /v1/memory/candidates,POST /v1/memory/candidates/{id}/promoteWho it is for · both
What it does. Two opt-in on-ramps, both default off, that stop memory being purely manual. Engrams, behind CORECRUXD_FEATURE_ENGRAM_MCP which is default off for the tool surface, are procedure overlays resolved at the start of a session so an agent begins with the operating instructions for the job rather than discovering them. Auto-capture, behind CORECRUXD_AUTO_CAPTURE which is also default off, runs a deterministic extractor with no model call over text and files you supply, and produces review-only candidates that a human promotes or rejects.
Why it works this way. The obvious product is automatic capture of everything an agent says, and it was rejected. Two reasons. A store filled by a model's judgement about what mattered dilutes recall until the deliberate writes are unfindable, and a capture path that stores without review is a data-protection problem that arrives silently. So the extractor is deterministic and inspectable rather than model-driven, and its output is a queue, not a write. The trade-off accepted is that both halves are off unless you choose them, and that the extractor's recall is bounded by rules rather than by comprehension.
What changes for you.
- As an operator: if you turn auto-capture on, you get a review queue, not new facts. Promotion is a human act, and nothing enters the store without it.
- As an agent: with engrams enabled, session start can hand you the procedure for the task instead of you inferring it, which is a different thing from recalling facts about the task.
What it does not do.
- It does not capture everything. There is no path in this edition that watches a conversation and writes what it thinks is important. Deliberate writes are the shipped story; gated extraction is the opt-in one.
- The extractor does not use a language model, so it does not summarise, infer or paraphrase. It finds what its rules can find.
- A candidate is not a fact. Until someone promotes it, nothing is recallable.
Turn it on. CORECRUXD_FEATURE_ENGRAM_MCP for the engram tool, default off; the engram HTTP routes are mounted regardless. CORECRUXD_AUTO_CAPTURE for extraction and the candidate queue, default off.
Where the detail lives. The import, capture and engram routes are api/01 §1.6; the tool contract is api/12 §12.4; the extractor entry point is memory_capture.rs:100.
Sources
- Fact record, journal format, durability boundary, compaction and privacy enforcement: daemon/10 The memory substrate.
- Fact, memory and engram routes: api/01 Facts and memory.
- Memory panel tools: api/12 §12.10.

