Capabilities · 9. Leaving
You can leave with your memory, offline, in one command, and verify the result on a machine that has never seen this daemon. Two of the four sections in that export are empty by design, the import path back in is behind a flag that is off, and the scorecard that grades your custody posture produces the same headline number no matter how your daemon is configured. All four of those sentences are true at once, and this chapter exists so you learn them here rather than at the point of leaving.
This chapter is explanation. It says why the exit path is shaped this way and exactly where it stops. Manifest fields, verification steps and CLI flags are reference and already exist.
9.0 Six capabilities, and the shape of the exit
The design commitment behind this family is unusual enough to state directly: the exit path is offline and CLI-first, and it does not depend on the daemon being healthy, licensed, or even running. That is a deliberate inversion of the normal arrangement, where export is an API you must be entitled to call. The cost of the inversion is that there is no HTTP export route for a memory pack at all.
| # | Capability | Status | The honest one-line summary |
|---|---|---|---|
| 9.1 | .cruxpack signed memory export | Build, sign and verify SHIPPED; two of four sections STUBBED | Your facts and sessions, as one self-certifying uncompressed JSON file |
| 9.2 | Pack verification and import planning | Verify and plan SHIPPED; HTTP import behind CRUX_MEMORY_IMPORT, default off | Seven hard rejects before a byte is applied, then a collision plan |
| 9.3 | Composed context export, verified offline | SHIPPED, unflagged | Memory pack plus audit bundle plus a signed manifest, re-checkable with no network |
| 9.4 | Streaming fact export and envelope import | SHIPPED | Cursor-paged incremental export over HTTP, for continuous mirroring |
| 9.5 | Custody scorecard | FLAG CRUX_CONTEXT_CUSTODY_AUDIT, default off, and largely constant when on | A ten-axis read of the architecture, not an inspection of your process |
| 9.6 | Crypto-shred envelope and destroy marker | STUBBED | Sealing and a marker exist; key destruction does not |
Grounding for each capability is in that capability's closing line rather than in a chapter-level list.
9.1 Taking your memory out as one file that proves its own origin
Status build, sign and verify are SHIPPED. The
entitiesandreceiptssections are STUBBED: schema-reserved and always empty. Reached throughcorecruxctl memory pack. Who it is for operators.
What it does. One command writes your visible memory to a single file: a manifest carrying the exporting daemon's install fingerprint, its passport fingerprint and its public key, a BLAKE3 content hash over the manifest and sections, and an Ed25519 signature over that hash. Facts are sorted deterministically and sessions by id, so two exports of the same store are byte-identical. Soft-deleted facts are excluded unconditionally, with no flag to override. Private facts and reserved-prefix entities are excluded unless the operator, having been shown a per-prefix scan of exactly what would be copied, types the literal phrase include private.
Why it works this way. The pack is designed to be trustworthy to someone who does not trust the daemon that produced it, which means the verification story has to survive the daemon being gone. Putting the exporting daemon's public key inside the signed payload makes the file self-certifying: a recipient can check that the signature matches the key, and that the key matches the fingerprint the manifest claims, without contacting anything. The source states the trust boundary candidly rather than overselling it: there is no network access and no PKI, so the pack certifies itself to a fingerprint, and whether to trust that fingerprint remains the recipient's decision. That is a smaller claim than "verified" and it is the true one.
Determinism is the second deliberate choice. Byte-identical exports of an unchanged store mean a diff between two packs is a real change rather than serialisation noise, which is what makes a pack usable as evidence rather than only as a backup.
The confirmation phrase for private data deserves its own note, because it is the pattern the rest of the product should copy. Exporting private memory is not a flag you can set once and forget in a script. You are shown a count per reserved prefix of exactly what would leave, and then you type two words. Declining aborts before anything is written. It is friction placed at precisely the point where friction is worth its cost.
What changes for you.
- As an operator: leaving is a single offline command whose output a third party can check. There is no export entitlement, no API to be granted, and no dependency on the daemon being healthy at the time.
- As an agent: nothing. This is an operator action on the operator's machine.
What it does not do.
- The
entitiesandreceiptssections are always empty. They are reserved in the schema and hard-coded to empty in v1. Your substrate graph and your receipt chain do not travel in a.cruxpack. If you export expecting your receipts to come with you, they will not; export them separately as an audit bundle, or use the composed bundle in 9.3. - It is uncompressed JSON, not an archive. Not a tar, not a zip, not a directory: the pack is serialised straight to the output path. Expect it to be large and to compress well, and expect to compress it yourself.
- There is no HTTP export route for a pack. Export is CLI-only, by design, which is the same design decision that makes it independent of daemon health.
- It does not export what was deleted. Soft-deleted facts are excluded with no override, and a pack that contains one is rejected on verification as tampered.
Where the detail lives. The manifest fields, the five exclusion rules, the determinism guarantee and the stubbed sections: Daemon 15.3. The section reservation and its reasoning: cruxpack.rs:164. The confirmation phrase: memory_pack.rs:33.
9.2 Checking a pack before you believe it, and planning before you apply it
Status verification and import planning are SHIPPED. HTTP import is behind
CRUX_MEMORY_IMPORT, default off, and returns 404 when unset. Reached throughcorecruxctl memory import;POST /v1/memory/import. Who it is for operators.
What it does. Verification applies seven hard rejects in order before a byte is applied: a schema gate, declared counts against actual section lengths, no soft-deleted facts present, a private-consistency check, a recomputed content hash, self-certification of the fingerprint against the embedded key, and finally the Ed25519 signature. Only then does planning run, showing which entity and key pairs already exist so collisions are visible before anything is written. Application goes through the journaled, receipted bulk write path, never a raw filesystem write.
Why it works this way. The ordering is the argument. Cheap structural checks run first so a malformed file fails fast, and the signature check runs last because a valid signature over a body that fails a structural check would be the more confusing outcome. The private-consistency check is the interesting one: a pack that contains private or reserved facts while declaring that private data was not included is treated as tampering, not as a warning. That is a deliberate refusal to be lenient about the one inconsistency that would let someone strip a disclosure from an otherwise valid file.
Planning before applying exists because import into a non-empty store is the normal case, not the exceptional one. Showing collisions first turns "will this overwrite my work" from a question you answer afterwards into one you answer before.
What changes for you.
- As an operator: you can check a pack someone else produced without trusting them, and you can see what an import would collide with before you run it.
- As an agent: nothing directly. Imported facts arrive through the ordinary journaled write path, so they behave like any other fact afterwards.
What it does not do.
- The round trip is not lossless, and this is the limit people are most likely to be surprised by. Every imported fact has its
source_receiptoverwritten with a reference to the pack. Fact ids, version numbers, storage timestamps and supersession links are not carried; the receiver mints new ones. The actor may be remapped. What arrives is the content of your memory, correctly attributed to the pack it came from, not a byte-level restoration of the original store. - Imports never overwrite. A colliding entity and key lands as a new version and is counted as a collision. That is safer, and it means a re-import grows your version history.
- HTTP import is off by default.
POST /v1/memory/importreturns 404 unlessCRUX_MEMORY_IMPORTis set. The CLI mirrors the gate. - Existing sessions are skipped rather than restored.
Turn it on. Set CRUX_MEMORY_IMPORT=1 only if you need the HTTP route. The CLI path is the intended one.
Where the detail lives. The seven rejects, the full lossiness table and the flag's off-behaviour: Daemon 15.3. The route and its shape: API 1.6. Verification entry point: cruxpack.rs:467.
9.3 One bundle that proves what you held and what you did
Status SHIPPED, unflagged. Reached through
corecruxctl context export,corecruxctl context verify. Who it is for operators.
What it does. One command composes a bundle directory containing the memory pack, an audit bundle and a signed manifest that binds both by hash and embeds an offline audit-verification report. The signed message is a deterministic newline-joined string built from the manifest's own recorded fields rather than from its JSON, BLAKE3-hashed and then Ed25519-signed. The verify command rebuilds that same signing input from the manifest, re-hashes both components, re-verifies the pack and re-runs audit verification, with no network and no running daemon.
Why it works this way. Signing the JSON would make the signature hostage to serialisation: key ordering, whitespace and library version would all become part of the security boundary, and a verifier written in another language would be fighting formatting rather than checking cryptography. Signing a deterministic string that the verifier reconstructs from named fields removes that entire class of problem, and it is what makes independent verification realistic rather than theoretical. Binding both component hashes into one signed manifest is the other half: it turns two files that happen to be in a directory together into one artefact where swapping either is detectable.
This is the capability to reach for when 9.1's stubbed sections are a problem. The memory pack does not carry receipts; the composed bundle carries the audit bundle alongside it and binds the pair.
What changes for you.
- As an operator: you can hand a reviewer one directory and a verify command, and they can check it on an air-gapped machine. That is a materially different conversation from "log into our console".
- As an agent: nothing. This is an operator artefact.
What it does not do.
- It composes existing artefacts. It does not fix 9.1's stubbed sections; the pack inside the bundle is still a pack, with the same empty
entitiesandreceipts. - The audit bundle it carries is a cross-reference list rather than receipt bodies. What travels is the ability to check that a receipt existed and matched, not the receipt payloads themselves.
- Verification confirms integrity and origin fingerprint. It does not confirm that the exporting daemon was honest about what it chose to include.
Where the detail lives. The bundle contents, the signing input and the offline verify path: Daemon 15.3. What audit-bundle verification does and does not establish: Daemon 13.10. Export contract: export.rs:14.
9.4 Leaving continuously rather than all at once
Status SHIPPED. Reached through
GET /v1/facts/export,POST /v1/result-envelope/import. Who it is for operators and agents.
What it does. A cursor-paged export streams facts stored since a given point, returning an opaque cursor to resume from. A separate import path accepts externally produced result envelopes. Together they support keeping a copy elsewhere that stays current, rather than taking a full snapshot each time.
Why it works this way. A full export is the right artefact for leaving and the wrong one for mirroring: it grows with your whole history, so mirroring by re-export costs more every day. Incremental export with an opaque cursor keeps the cost proportional to what changed. The cursor is opaque on purpose so that its internal shape is not a contract you can accidentally depend on, which leaves room to change how paging works without breaking every consumer.
What changes for you.
- As an operator: your escape hatch does not have to be a periodic full dump. You can hold a continuously current copy off the box.
- As an agent: this is a synchronisation primitive, not a recall path. Use the ordinary fact query surface to read memory.
What it does not do.
- It is not a signed artefact. Unlike 9.1 and 9.3, a streamed export carries no manifest, no content hash and no signature. It is data movement, not evidence.
- It exports facts. Sessions, entities and receipts are not on this path.
- Do not construct or parse the cursor. It is opaque and its shape is not a contract.
Where the detail lives. Export parameters, paging and the envelope import shape: API 1.1 and API 1.5. Handler: facts.rs:735.
9.5 The custody scorecard, and why its headline number is a constant
Status FLAG
CRUX_CONTEXT_CUSTODY_AUDIT, default off, and largely constant even when on. Reached through MCPcontext_custody_audit. Who it is for operators.
What it does. Behind CRUX_CONTEXT_CUSTODY_AUDIT, default off, the tool emits a ten-axis scorecard against a "can I leave" exit test, together with a headline lock_in_risk score and a list of trust recommendations. Its inputs are four environment reads, two values off the request context and a fact count.
Why it works this way. The intended reading, which the source argues for explicitly, is that several of these axes are properties of the architecture rather than of your configuration. Export really is always available, because the CLI path in 9.1 does not depend on any flag. Private facts really are never pushed by sync, whatever your remote settings. On that reading, marking those axes as strong regardless of flag state is not a bug; it is the tool declining to downgrade a guarantee that flags cannot affect. The module's own note says the same thing from the other side: verdicts reflect runtime flags, and a flag-gated-off capability is reported as partial rather than strong.
What changes for you.
- As an operator: three of the ten axes tell you something about the process you are running.
CHECKmoves with receipt verification,REVOKEmoves with passport revocation, andROUTEmoves with whether a capability router is wired. Those three are worth reading. The trust-recommendations list is also live and tells you which production-recommended flags are still off. - As an agent: the tool is off by default, so assume it is unavailable unless you have been told otherwise.
What it does not do.
- It does not inspect anything. No file is opened, no export is attempted, no pack is built or verified, no receipt is checked and no sync state is probed. It reads flags and returns a scorecard.
- Seven of the ten verdicts are constants.
SEE,DO,REMEMBER,EXPORT,INSPECT,KEEP-LOCALandPROVEare hard-coded to strong and will say so on any daemon in any configuration. - The headline
lock_in_riskis structurally always 1. It starts at 1 and increments only whenEXPORTorKEEP-LOCALare not strong, or whenROUTEisnone; the first two are constants andROUTEnever producesnone, so every increment is dead code. The project's own test asserts the value is 1 with every flag switched off.lock_in_labelis consequently always "trivial to leave". - It follows that the scorecard is a statement about how the architecture is designed, not a result computed from your daemon. Read it that way and it is a fair summary. Do not put
lock_in_riskin front of a risk reviewer as an assessment of your deployment, because the same number is produced by a daemon with every capability enabled and by one with none, and no configuration you can apply will change it. A constant presented as a measurement is exactly the failure this product exists to cure.
Turn it on. Set CRUX_CONTEXT_CUSTODY_AUDIT=1 if you want the tool. Read the axis table before you quote the output.
Where the detail lives. The full input list, the per-axis verdict sources and the dead-increment analysis: Daemon 15.4. The tool schema: API 12.27. Scorecard construction: context_custody_audit.rs:134.
9.6 Crypto-shred, which is sealing and a marker rather than destruction
Status STUBBED. The module is explicitly non-destructive. There is no key registry and no key-destruction path. Reached through library only. No HTTP route, no MCP tool, no CLI command. Who it is for operators evaluating an erasure story.
What it does. The module seals subject-scoped payload bytes under a caller-supplied content-encryption key and proves that the retained ciphertext cannot be opened without that key. A separate destroy marker records one of two states: that destruction was requested, or, with a human-gate receipt, that destruction was attested. Both halves are primitives. Nothing invokes them in the running daemon.
Why it works this way. Crypto-shredding is a legitimate technique and a genuinely hard one to ship, because the guarantee rests entirely on custody of the key: destroying it must be reliable, auditable and irreversible, and the system must be able to say afterwards which keys were destroyed and when. Shipping the sealing primitive and the state vocabulary while explicitly not shipping the destruction path is the honest ordering. The alternative, wiring a destroy call that marks a record and does not destroy a key, would produce a system that reports erasure it has not performed, which is worse than having no feature at all.
What changes for you.
- As an operator: nothing yet. If your compliance position depends on crypto-shredding, this does not provide it, and you should plan on the erasure path in the next block instead.
What it does not do.
- It does not erase anything. The module is deliberately non-destructive. Production key destruction is described in the source as a separate, human-gated operation that is not implemented here.
- There is no key registry. Nothing tracks which content-encryption keys exist, so nothing can enumerate what a destruction would cover.
- The envelope retains the plaintext hash in clear. That is a fingerprint of the original content sitting alongside the sealed bytes. It is fine for integrity checking and it is not what you want if your threat model includes an adversary confirming a guess about the plaintext.
- The destroy marker records a claim about a state. It is not evidence that a key was destroyed, because no code path destroys one.
- It is not the erasure path this daemon actually has. Real deletion here is soft-delete followed by compaction, which rewrites the journal with value-free tombstones, optionally driven by a retention sweep and blockable by a legal hold. Crypto-shred is an alternative approach to the same problem, and it is not built.
Where the detail lives. The erasure that does work, compaction and tombstones: Daemon 10.6. Legal holds: API 4.7. The non-destructive contract and the two marker states: crypto_shred_v1.rs:8.
Sources
Grounding for each capability is carried in that capability's closing line, next to the claim it supports, rather than gathered here. Every source link resolves against origin/main of the Crux repository.

