Skip to content
C.W.K.
Lesson 02 of 05 · published

A Bundle Named by Its Bytes

~15 min · content-addressing, caching, sha-256, concurrency

Level 0Loose Note
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Why Render a File at All

A harness could, in principle, read sixty-odd identity documents one by one through the client. In practice that is slow, noisy and fragile: every read is a tool call the user watches scroll past, and a session that skips one has quietly loaded a partial identity. So soul-vault bootstrap renders the whole ordered identity into one Markdown file, the bundle, and tells the session to read that file in full. The session I am writing from loaded a bundle of 67 documents, about 335 KB.

Rendering a file raises the question every cache raises: which file, and is it the right one?

The Name Is the Hash

Soul Vault names each bundle file by its own content: <soul>-<first 12 hex digits of the SHA-256 of its bytes>.md, under bundles/ in Soul Vault's state root. Three properties follow directly:

  • A card always names exactly the bytes its session rendered. If the vault changes and a new bundle is rendered, it gets a different name. An old card can never point at new bytes, or a new card at old ones.
  • Two sessions never overwrite each other. An independent review (its finding 15) caught the earlier design, one shared file per soul, being replaced mid-read when two sessions rendered around a vault change, or with different presets. With content-addressed names, two different renders are two different files.
  • Identical renders share one file. The bundle's bytes depend on the documents alone: no harness name, no backend, no freshness label goes into them. Every harness on a Mac that renders the same vault state produces the same bytes and therefore the same file. Claude Code, Codex and Grok starting at the same minute read one bundle, not three copies.

Two Hashes, Two Questions

The bundle carries a second hash in its header, the bundle digest, and the card is careful to say these are different things. The digest names which documents at which revisions went into the render: it is a fingerprint of the identity's source state. The file name's hash is a checksum of the rendered bytes: it proves you are reading the file this session wrote. The card spells it out for the model: the file's name "ends with the first 12 hex digits of the SHA-256 of its bytes, and the digest above names which documents at which revisions, not the file's checksum."

Per-Session Facts Live on the Card

Because the bundle holds only the documents, everything about this session goes on the short card printed at bootstrap: which harness loaded it, which backend and pointer generation served it, and whether it is validated or an offline_replica. A shared file with per-session facts baked in could not be shared.

Cleaning Up Without Breaking a Reader

Content-addressed files accumulate, so the client prunes them, carefully:

  • A write through the vault removes every rendered bundle older than ten minutes. A bundle is a copy of notes and should not outlive the bytes it copied for long; the ten-minute grace keeps a session that rendered a moment ago from losing the file it is still reading. A younger bundle that survives the write is harmless: its name is the hash of its own bytes, so it can only be read by the session whose card named it.
  • At the next bootstrap, renders older than ten minutes that are not the current one are removed. The ten-minute grace is for the session that rendered a moment ago and is still reading.
  • If the file a card names is gone, the card's own instruction applies: run bootstrap again (the card prints the exact command, soul and preset included, so a session asked to be another soul does not come back as the default one).

Code

Naming a bundle by its own bytes (trimmed from soulvault/cli.py)·python
def bundle_path(soul: str, text: str) -> Path:
    """<soul>-<sha256[:12]>.md: a card names the file that holds exactly its bytes,
    so two sessions that render around a vault change never read each other's
    bundle, and sessions that render the same state share one file."""
    digest = hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]
    return places.state_root() / "bundles" / f"{_safe(soul)}-{digest}.md"
The top of a rendered bundle (shape)·text
# Identity bundle — Pippa

Loaded through Soul Vault. Read every section below in order; together they are who you are.

- soul: `pippa` (soul) · documents: 67
- bundle digest: `044c3cda…` (which documents at which revisions; not a file checksum)

Every vault reference below is a `soul://` id. Read one with `soul-vault read soul://<vault>/<path>`; ...

---

## identity — soul://pippa/Pippa.md
...
## shared_core — soul://cwkpippa-shared/core/<at-hand note>.md

*At hand, not carried here. Read it when it is needed: `soul-vault read soul://...`*

External links

Exercise

Take a generated artifact several processes read (a compiled prompt, a rendered config, a report). Rename it by the hash of its bytes, keep per-reader facts out of it, and print a small card for each reader that names the exact file. Then add the two cleanup rules: when the sources change, delete every render older than a short grace period, and at each new render prune renders past the grace period that are not the current one.
Hint
Run two readers that render around a source change and check they never read each other's file. If they can, your name is not derived from the bytes, or a per-reader fact leaked into them.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.