Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Repository Layout and Authority

This page is the authoritative current-state reference for Prikk’s on-disk repository layout and storage authority boundaries. It describes the current implementation and is grounded in the code, released RFCs, and implementation status records listed in the anchor table at the foot of the page.

For logical object concepts, see the data model. For repository path and worktree write-safety rules, see path and worktree safety. For local persistence and recovery behavior, see durability and crash recovery. For trust and signature scope, see the trust and threat model. For lock and ref compare-and-swap behavior, see concurrency and locking. For format stability, migration limits, and release identity, see release, versioning, and compatibility.

Core Caveats

  • Prikk is early implementation software and is not a production Git replacement.
  • .prikk/ is Prikk’s native repository format and is not Git-compatible storage.
  • .prikk/FORMAT is a current format-version gate, not a stable-format or migration guarantee.
  • Ref files are mutable pointers for convenience and recovery, not roots of trust.
  • Cache-like directories are initialized today, but are not current roots of trust; the quarantine directory is retired and no longer initialized.
  • Durability and recovery claims are supported by current unit and integration tests, not by a completed crash-matrix or fuzzing campaign.
  • Repository mutation is exercised by project gates on Linux, macOS, and Windows (DC-87 Stage 2). Windows’ anchoring guarantee is weaker than Linux/macOS in one stated way — see platform support for the exact gap and which of the eight live durability guarantees (G1–G4 and G6–G9 in crates/prikk-store/src/foundation/fsutil/contract.rs; G5 was retired in DC-98) are held, weaker, or documented no-ops there. Read-only commands are CI-gated on macOS and Windows too — see platform support.
  • Stable repository-format migration, garbage collection, quarantine enforcement, cache rebuilding, hosted forge trust, and remote-tracking remain deferred. prikk sync (RFC 116, RFC 117) and prikk merge (DC-74) have since shipped — see the sync and merge guides.

Initialized Layout

A fresh prikk init creates the repository directory, the initialized directories below, the format marker file, and the leaf files the default session and the write state need before their first write: worktree.marker, worktree.provisional, current-branch (heads/main), active/default/queue.wal, active/default/ref-name, active/default/declarations, active/default/witness, and recovery/log (crates/prikk-store/src/foundation/layout.rs:280-305, and ensure_write_state at layout.rs:740-751). The active WAL and active ref metadata are therefore empty when init returns. Ref, received-ref, and trust storage are fixed, named container files allocated by init itself, empty until first use — there is no per-ref, per-received-ref, or per-key-id file or directory created later, since none of those names exist at init time and a per-name file would have to be.

.prikk/
  FORMAT
  current-branch
  worktree.marker
  worktree.provisional
  recovery/
    log
  containers/
    patch/{a,b}.container
    block/{a,b}.container
    ref-state/{a,b}.container
    tag/{a,b}.container
    attestation/{a,b}.container
    blob/{a,b}.container
    index.container
    generations.log
  active/
    default/
      queue.wal
      ref-name
      declarations
      witness
  refs/
    containers/
      log-{a,b}.container
      pointer-index-{a,b}.container
      pointer-index-generation.log
      received-index-{a,b}.container
      received-index-generation.log
    locks/
  trust/
    keys.container
    policy-{a,b}.container
    policy-generation.log
  cache/

Every file above is created by init. Each is empty until first use, except FORMAT (its version) and current-branch (heads/main). No name under .prikk/ is created after init — that is a design invariant, not an implementation detail, and it is what makes the repository durable on filesystems that cannot make a new directory entry durable. The exceptions are the write-state files a repository created before they existed lacks: recovery/log and the default witness, which the first write creates exclusively (layout.rs:740-751), and the declarations file, which a write also self-heals (layout.rs, the RFC 144 §4o.2 comment in init). recovery/ is never authority (next paragraph); init creates its log, and a repository created before 0.49.0 gets the log and the witness at its first write.

recovery/log holds the bytes a repair removed, appended and flushed before the repair truncates. Each repair is one run, and each file it changes is one entry in that run. Every repair writes here and nowhere else. Each entry records its source file, the offset it was cut at, the removed bytes, a hash of the file before that offset, and the identity of the files that give those bytes their meaning. A repair names its run, for example recovery/log, run 3f9a0c1e8b2d4a57. A rewrite in place (the commit witness, ref-name) keeps the file’s previous bytes too, so a run can be undone exactly. It exists so that a repair can be wrong about what it removed (a torn tail and a lone damaged record look the same) without anything being lost. It is never authority: no classification reads it, and verify reports damage in it on its own line without changing its exit status. The log is never truncated except by prikk doctor --recovery-clear, which removes the saved content for good. It is not quarantine/, which is retired.

The three commands:

  • prikk doctor --recovery-list prints the saved entries grouped by the repair run that wrote them: a line per run with its id and entry count, then each entry’s source and its label. A Cut entry (trailing bytes a repair removed) prints the offset it was cut at and its length; a Replace entry (the whole file’s own previous bytes, before a repair rewrote it in place — --keep-slot’s own saves, and the commit witness/ref-name rewrite) prints “replaced, N bytes saved” instead (0.51.0 step 1 Part C2, C4: “cut at 0” read as a truncation even when nothing was removed). It does not judge whether an entry can be restored.
  • prikk doctor --recovery-restore <run id> [--plan-only] undoes the whole run, in reverse order: it prints its plan first (each step, and its conditions), then writes only when every condition holds. A step that already holds its result is skipped, so an interrupted restore finishes when it is run again. A later run that changed the same files is named, and the restore waits for it. It writes only when the source is exactly as long as the recorded offset, its bytes before that offset hash to the recorded hash, and the files that give the bytes their meaning are unchanged.
  • prikk doctor --recovery-clear [--plan-only] lists what it will remove, then empties the log.

Repairs written before 0.49.0 are in recovery/ as *.bytes files. They are listed as older files, read or deleted by hand, and never restored.

init also creates refs/tmp/, which is allocated but never written to. It is a remnant of a retired candidate-publication mechanism, not authority for anything, but its absence is an error: verify scans it on every run, so repository open validates it is present.

Ten further directories that were previously allocated at init for the same reason — objects/ and its six object-type subdirectories, quarantine/, refs/by-id/, refs/logs/ — are no longer created. Nothing has ever written into any of them (object and ref storage moved into containers years earlier), and nothing validates their presence at open, so a repository initialized before this change keeps them harmlessly and a newly initialized one simply lacks them. One dormant diagnostic still reads the objects/ tree if present — see Object Store below.

New repositories contain 7 in FORMAT; a repository created by 0.44.0 or earlier contains 6, still opens, and moves to 7 in place with prikk format upgrade (see Release Compatibility). Formats 1 through 5 are rejected at open, with an error naming the format found and directing migration through prikk bundle export on a version that still supports it; so is any number this build does not know. The format marker is a gate: a repository whose on-disk shape does not match what the code expects is refused rather than opened and left to fail later.

cache/ holds rebuildable, non-authoritative state — a corrupt or absent cache file is never an error, and any operation’s result is identical whether the cache is warm, cold, or missing.

There is no initialized gc/ directory, and prikk performs no garbage collection: no object is ever deleted or superseded once written.

Object Store

Objects are not stored one-file-per-object. Every persistent object envelope is a checksum-framed record appended into a shared, per-type container allocated at init:

containers/<object-type>/a.container

with a paired b.container allocated at init and permanently unused — object containers have no data model to compact against (see Compaction below) — and one containers/index.container mapping each object id to the container, slot, offset and length where its record lives.

Six object types are persisted this way — patch, block, ref-state, tag, attestation, blob. RefUpdate is stored inline in ref logs, not as an object record.

Why containers rather than files. An object’s identity is its content hash, so every one-file-per-object write created a new directory entry. Making a new name durable requires an fsync on the parent directory, which POSIX provides and Windows does not — there is no documented or undocumented Windows primitive that makes a new directory entry durable. Appending to a file that already has a name needs only content durability, which every supported platform provides. Moving objects into fixed, init-allocated containers is what makes prikk’s durability guarantee statable as a property rather than as a list of platforms that happen to pass.

Reading is isolate-and-continue. A damaged record is named at its byte offset and the scan continues to the next intact one, so corruption is confined to the records it actually damaged rather than failing the whole container. A record is authority only when its frame checksum verifies, its envelope decodes and validates, it has the expected type, and its computed content-addressed id matches the requested id.

The index is rebuildable and off the durability path. An object record is appended and made durable before its index entry is appended, so a crash between the two leaves an object present but unindexed — recoverable by rebuilding the index from a container scan. The reverse ordering would let a reader see a valid index entry pointing at bytes that are not there, so the ordering is load-bearing.

Nothing deletes an object, and nothing is rewritten. Writing an id that already exists with identical bytes is a no-op. In format 7 an id may hold several records, and the last one is authoritative: another signer’s copy of a stored object — the same type, schema and payload under other signatures — is written as one superseding record carrying the union of both signature sets, and the index’s last entry for the id points at it. Every reader resolves an id to that last record, in every pass. The union is decided under the object-store lock against the record as stored at that moment, so two writers merging into one id keep both signature sets. A copy whose payload differs under the same id is corruption and is refused in every format; in format 6, any different copy is refused. Containers therefore only ever grow.

objects/ and its six type subdirectories are a retired one-file-per-object layout, replaced by the containers above; nothing in a format-3 repository writes into it, and init no longer creates it. A repository initialized before this change may still have it on disk — its presence or absence is not validated at open — and if so, one dormant diagnostic (scan_loose_file_temp_debris, kept for .pobj.tmp. debris a format-3 repository can no longer produce) still reads it during verify.

Refs and Ref Logs

Ref pointer and ref-log storage is shared, not per-ref: every ref’s own pointer entry and log records live in containers allocated once at init, not in a file named for that ref. A ref name does not exist until branch create/tag create mints it, well after init — a per-ref file could never be one of init’s own fixed names, so pointers and logs for every ref instead interleave inside:

refs/containers/pointer-index-{a,b}.container
refs/containers/log-a.container
refs/containers/log-b.container
refs/locks/<ref-name-storage-key>.lock

Locks remain per-ref files, one per ref name actually in use, unaffected by this: the storage key is the hex SHA-256 digest of the ref name bytes, the same digest used internally to attribute each container entry to its own ref.

pointer-index-{a,b}.container is an append-only, checksum-framed sequence of pointer entries; each entry records one ref’s human-readable name, its published RefState object id, and the SHA-256 key derived from that name. A ref’s current pointer is its last matching entry in the live slot — republishing a ref appends a new entry rather than rewriting the old one, and which slot is live is named by this container’s own generation log, not always a (see Compaction below). It is useful for lookup and recovery, but is not trusted alone: verification checks pointer content against RefState objects and ref-log evidence, the same as before.

received-index-{a,b}.container holds the same shape of entry for received refs — pointers imported by prikk bundle import under the separate remotes/<name> namespace (DC-78 §D4), never refs/by-id/: an imported RefState object keeps the origin repository’s own embedded ref name, which could never agree with a locally renamed pointer, so received refs get their own container and key space rather than a special case in the ordinary one. Last-entry-wins, the same as the ref-pointer index, and compacted the same way. This index is never consulted by verify_repository directly — every object a received pointer leads to is checked by the ordinary object-store scan regardless of how it was discovered.

log-a.container/log-b.container hold every ref’s RefUpdate log records, interleaved by append order; a reader filters to one ref’s own subsequence by that same per-record key. Each record carries a signed RefUpdate envelope inline with frame magic, versioning, length, and checksum. Ref logs are publication evidence when their record chain, referenced RefState objects, target Blocks, signatures, and trust policy checks all hold — unchanged in meaning, only in storage shape. Slot b is allocated alongside slot a at init and permanently unused: the ref log is DC-38/DC-69’s audit trail and must never be compacted, so writes always target slot a (see Compaction below).

refs/locks/*.lock files are local synchronization files for ref-specific publication and repair, not a root of trust. There is no longer a temporary-candidate mechanism: an append-only pointer entry has no candidate value to stage before becoming durable, so a publish that used to write, sync, and promote a candidate now durably appends the pointer entry directly, in one step.

refs/by-id/ and refs/logs/ are no longer initialized directories — nothing has read or written either since ref publication moved into containers, so init no longer creates them, and a repository missing them behaves identically to one that still has them from before this change (their presence or absence is not validated at open).

refs/tmp/ is different and is genuinely required. init still allocates it, and verify lists it on every run, so a repository missing it fails verification with directory is absent: refs/tmp. Nothing has written into it since ref publication moved into containers, so the scan can only ever find nothing — but the directory must exist for the scan to succeed.

Active Session

The default active-session paths are:

active/default/queue.wal
active/default/active.lock
active/default/ref-name

These files are runtime-written, not guaranteed members of a bare initialized repository.

queue.wal stores exact signed Patch envelopes before sealing. WAL records are load-bearing local session state: they are the pending changes that seal replays and publishes, but they are not sealed history until publication succeeds.

ref-name records which local branch ref owns a non-empty active WAL. Missing or malformed active ref metadata on a non-empty WAL is an integrity issue because seal must not guess the publication target.

active.lock is a local synchronization file. It prevents concurrent active-session writers, but it is not evidence of repository history or trust. Stale lock cleanup after a crash is manual today; see concurrency and locking for the current operator boundary.

Trust Store

The repository-local MAINTAINER trust containers are:

trust/keys.container
trust/policy-{a,b}.container

Both are allocated empty at init, then appended to by prikk trust maintainer add/remove — no name is created after init.

keys.container holds one append-only entry per adopted key id: the key id and its Ed25519 public key. It has no slot pair and is never compacted — TOFU history must persist across key removal (see Compaction below).

policy-{a,b}.container holds a sequence of complete policy snapshots in its live slot (named by this container’s own generation log, not always a) — each add or remove appends the entire current adopted-key-id list, not an incremental change; readers take the last complete snapshot, with required = 1 meaning any one adopted key’s signature suffices (never stored — it is a constant, not configurable). Seal checks the configured MAINTAINER signer against this repository-local policy before publication, and verify checks publication envelopes against the same local trust boundary.

This trust store is authority for current publication-trust checks. It is not remote trust, global identity, key rotation, hosted forge policy, or a multi-maintainer threshold system. Key revocation is supported (prikk trust maintainer remove): a removed key’s material is retained internally (so a different key presented later under the same id is still refused), but it no longer counts toward the adopted set or reserves its case-folded id.

Compaction

Three containers accumulate entries that are superseded the moment a newer one lands: pointer-index-{a,b}.container, received-index-{a,b}.container, and policy-{a,b}.container. A ref’s or received ref’s pointer is only ever its last matching entry — everything earlier for the same key is dead weight from the moment it is superseded — and a trust policy snapshot supersedes every earlier snapshot outright, since each one already carries the complete adopted-key-id list.

Each of these three containers is paired with its own generation log (pointer-index-generation.log, received-index-generation.log, policy-generation.log, all under the same directory as the container they belong to) recording which slot — a or b — is currently live. A reader resolves the live slot by reading the last complete record in the generation log, not by assuming a.

An empty (or absent) generation log is trusted to mean “no compaction has ever run” only while slot b itself holds no data (0.50.0 step 1 Part E4, 019 §5.7, corrected by handoff 165, 020 §3.1). Slot b is never written except alongside the one generation record that names it live (compact.rs, and, for the pointer index only, prikk doctor --rebuild-pointer-index’s own rebuild), so an empty log over a non-empty slot b can only mean a compaction (or rebuild) genuinely happened and its own record of that fact was lost afterward — not that one never happened. Rather than refuse that state outright, every reader and writer deduces the live slot, testing content in both directions: one slot is stale beside the other when either it equals, or is a prefix of, a compaction of some earlier prefix of the other’s own history (a crash before the switch was recorded, or a write cut short), or the other slot starts with a full compaction of it and simply kept taking writes after. Whichever one direction holds names the live slot. Both directions holding and the two slots folding to the same entries means either reading is correct. Neither direction holding, or both holding while the folds disagree, means the state is genuinely ambiguous — proven, not merely undecided: two equally honest command histories (add a key, compact, remove it; or compact again, add the key back) can leave byte-identical slot files with opposite answers on whether a key is trusted, so no rule reading only the slots can be right in both. This refuses with its own distinct, typed error (PrikkError::AmbiguousGenerationLog, never confused with a damaged record). 0.51.0 adds a way out the user can choose, named first: for the received index and the trust policy container, prikk compact --received-index|--trust-policy --keep-slot a|b [--plan-only] prints each slot’s own entry count and folded state (for the trust policy, the keys trusted in one slot and not the other, by name) and which slot content would deduce or that the state is ambiguous, then — unless --plan-only — treats the chosen slot as live and compacts it into the other, saving what it overwrites first, the identical save-before-write ordering an ordinary deduced compaction already uses. A whole-.prikk/ backup (the trust policy text also says to re-apply every trust change made since) stays the second way out; for the ref pointer index, --keep-slot does not apply at all (its own rebuild never reads either slot as live, so there is no choice to make) — the rebuild remains its one way out. The earlier, one- directional version of this rule (Part E4, tested only whether b derives from a) is what review 020 found wrong: after an even number of compactions, where a is actually the newer slot, a superseded entry left in b defeated that one-directional test and answered with the stale slot anyway. Two still-narrower rules were tried and rejected earlier still: bare membership (Part E2) lets a repeated value, such as a maintainer revoked and later re-trusted, fool the test into resolving to the wrong slot; comparing only against the current content of one slot (Part E3) is wrong once a crash lets that slot keep taking writes afterward. prikk verify and prikk doctor warn whenever the state is merely deduced (not ambiguous), naming the container, the deduced slot, and the exact command to end it. For the received index and the trust policy container, that command is prikk compact --received-index/--trust-policy, and that compaction now also saves the slot it is about to overwrite, and the generation log, as one recovery run first — both saves land before either file is written, since a slot’s own meaning file (for these two containers) is its generation log, read while it still holds its own before bytes — restorable byte-identically until a later write touches either file. For the ref pointer index, compact --pointer-index instead refuses outright in the deduced state, naming prikk doctor --rebuild-pointer-index --plan-only, then the rebuild: the rebuild already saves correctly (re-deriving from the ref log, reading neither slot as live), so there is nothing a compaction’s own save would protect that the rebuild does not already handle, and naming one way out rather than two avoids a second, narrower save path entirely. A restore from the recovery log refuses outright instead of deducing, in either the deduced or the ambiguous state: it is a deliberate writer, and a stale meaning file could otherwise compare unchanged and pass a restore that should not proceed. For the ambiguous state too, prikk doctor --rebuild-pointer-index is the pointer index’s own way out, for the identical reason: it re-derives from the ref log without reading either slot as live, unaffected by the ambiguity.

prikk compact --pointer-index|--received-index|--trust-policy|--all reclaims the dead entries for one or all three: it reads the currently-live slot, keeps only what is still current (the last entry per key for the two indexes, the last snapshot for the trust policy), writes that reduced set to the other slot, makes it durable, and only then appends a generation record naming the new slot live — so a crash at any point before that append leaves the previous generation fully authoritative, and retrying the compaction from scratch is always safe. prikk compact ... --plan-only reports what a real run would reclaim without writing anything. Compaction never runs automatically; nothing else in Prikk invokes it.

Compacting one of these containers takes the same per-container lock its own writers take (see concurrency and locking), so a compaction run and an ordinary write to the same container cannot interleave, and a real run and a --plan-only preview never report stale numbers against each other.

Two container families never compact, deliberately — not because rotation is merely pending. Object containers (containers/<type>/{a,b}.container) have no data model to compact against: nothing is ever superseded or deleted (see Object Store above), so there is nothing for a second slot to reclaim. The ref log (refs/containers/log-{a,b}.container) is DC-38/DC-69’s audit trail, which must never be compacted. Both keep their b slot allocated at init and permanently unused. The trust key container (trust/keys.container) has no slot pair at all — TOFU history must persist across key removal, so it stays a single append-only file, never compacted.

Authority Model

Path or dataClassificationCurrent meaning
.prikk/FORMATFormat gateRequired by repository open; 7 is the current format. Format 6 still opens and is written as format 6 until prikk format upgrade moves it to 7 in place (marker only; no downgrade). Formats 1-5 are rejected at open, with no bridge and no in-place migration.
.prikk/configLocal settings, optionalWritten only by prikk config set (unset removes the file); absent means every key takes its default. One key today, incoming.max-object-bytes. Never authority for history and never read from the worktree or from anything imported, so nothing received can set a value in it; bundle verify, which opens no repository, does not read it, and an older prikk build ignores it.
containers/<type>/a.containerContent-addressed object authorityOne checksum-framed record per object in format 6; in format 7 an id may hold several, and the last is authoritative. Authority when the frame checksum verifies, the envelope validates, and its computed id/type match the requested object. objects/ is a retired remnant no longer created by init; authority for nothing.
refs/containers/log-a.container (log-b.container reserved)Publication evidenceShared, append-only signed RefUpdate records for every ref, interleaved; authoritative only with valid chain, object, signature, and trust checks.
trust/policy-{a,b}.container and trust/keys.containerRepository-local trust authorityCurrent local MAINTAINER trust input for seal and verify publication-trust checks. policy-{a,b} is compacted by prikk compact --trust-policy (the live slot is named by its own generation log); keys.container has no slot pair and is never compacted — TOFU history must persist across key removal.
active/default/queue.walLocal active-session statePending signed Patch envelopes before seal; load-bearing for the active session, not sealed history.
active/default/ref-nameLocal active-session metadataIdentifies which ref owns a non-empty active WAL; not sealed history.
refs/containers/pointer-index-{a,b}.containerMutable convenience pointerShared, append-only, last-entry-wins RefState pointer for every ref; fast current-state lookup and recovery target; not trusted without RefState/ref-log checks. Compacted by prikk compact --pointer-index; the live slot is named by its own generation log, not always a — see Compaction.
refs/containers/received-index-{a,b}.containerMutable convenience pointerSame shape as the ref-pointer index, for imported remotes/<name> refs (prikk bundle import); not consulted by verify_repository directly. Compacted by prikk compact --received-index.
*-generation.log (pointer-index-, received-index-, policy-)Compaction stateRecords which slot (a/b) is currently live for its own container; empty means a. Not history or trust evidence — see Compaction.
active/default/active.lock, refs/locks/*.lock, and the four container locks (pointer-index.lock, log.lock, received-index.lock, policy.lock, all under refs/containers/ or trust/)Local synchronizationPrevent concurrent writers, and (for the container locks) a concurrent prikk compact run; not history or trust evidence. Recoverable after a crash via prikk unlock — see concurrency and locking.
cache/Initialized, rebuildable, non-rootNever authority; a corrupt or absent cache file is not an error and does not change any result.
refs/tmp/Initialized, unwritten, requiredinit still allocates it; nothing writes into it since ref publication moved into containers, but verify lists it on every run, so its absence fails verification. Not authority for anything.
objects/ (and its six type subdirectories), quarantine/, refs/by-id/, refs/logs/Retired, no longer initializedinit no longer creates these; nothing has written into any of them since object and ref publication state moved into containers. Not validated at open, so a repository initialized before this change keeps them harmlessly. Not authority for anything. objects/ alone still has one dormant reader — see Object Store.
recovery/logCreated by init (and at the first write to an older repository), never authorityThe bytes every repair removed, one run per repair, appended durably before the truncation. Listed, restored and cleared by prikk doctor --recovery-list, --recovery-restore and --recovery-clear. Older recovery/*.bytes files from before 0.49.0 are listed separately.
gc/Deferred/not presentNo current initialized directory or released behavior.

Deferred and Not Stable

Prikk does not provide in-place or history-preserving migration between any two formats. The documented writable path is a newly initialized repository, under whichever format is current (see the Authority Model table above), followed by deliberate worktree re-authoring, which creates new NodeIds, objects, signatures, and history. Copying .prikk/ data or editing FORMAT is not migration. This explicit transition does not promise general format stability.

prikk sync (RFC 116, RFC 117), prikk merge (DC-74), and single-ref prikk bundle export/verify/import (DC-44) have since shipped — see the sync, merge, and backup and restore guides. Still deferred: garbage collection, cache rebuild semantics, quarantine enforcement, stable repository-format migration, multi-ref backup export, a rehearsed repository-format-migration restore, remote trust, hosted forge semantics, complete branch management, remote-tracking, and full cross-platform filesystem validation.

Claim-to-Source Anchors

ClaimSource anchors
Repository initialization creates the listed directories and writes .prikk/FORMAT.layout.rs, DC-31
.prikk/FORMAT selects format 7 for new repositories; format 6 still opens and upgrades in place; formats 1-5 are rejected at open.layout.rs, DC-40
Persistent objects are checksum-framed records appended into per-type containers allocated at init.layout.rs, object_store.rs
Six object types currently have initialized persistent object directories; RefUpdate is inline-only in ref logs.layout.rs, object_store.rs, refs/container.rs
Ref storage keys are SHA-256 hex digests of human-readable ref names, shared by the pointer index, the log container, and per-ref lock files.layout.rs, refs.rs
The ref-pointer index is a shared, append-only, last-entry-wins container holding every ref’s own current-pointer entry (name, RefState id, storage key).refs/pointer_index.rs, refs.rs, data model
The ref-log container is a shared, append-only sequence holding every ref’s own signed RefUpdate envelopes, interleaved, with frame magic, checksums, and per-ref replay semantics.refs/container.rs, refs.rs, durability and crash recovery
init creates the default active WAL (queue.wal) and active ref metadata (ref-name) empty; their contents are runtime active-session state.active.rs, wal.rs, durability and crash recovery
Trust policy and maintainer public-key files are written by the trust command and define current repository-local MAINTAINER trust.trust.rs, layout.rs, security and signing setup
Verification checks object placement, ref pointer/log consistency, active WAL state, and publication trust within current limits.verify.rs, integrity and recovery diagnostics, trust and threat model
cache/ is initialized but not a root of trust; quarantine/ is retired and no longer initialized, and gc/ is not an initialized directory.layout.rs, DC-31
The received-ref index is a shared, append-only, last-entry-wins container for imported remotes/<name> pointers, kept separate from refs/by-id/ because an imported RefState’s own embedded ref name can never agree with a locally renamed pointer.received.rs, received_index.rs
Three containers (ref-pointer index, received-ref index, trust policy) each have a generation log naming which slot is live, defaulting to a when empty and slot b itself holds no data – an empty log over a non-empty slot b deduces the live slot by testing both directions (is either slot a compaction of some earlier point in the other’s own history, or did the other take real writes after a full compaction of it; handoff 165, correcting 0.50.0 step 1 Part E4’s one-directional test), warning (verify/doctor) rather than refusing when one direction decides it; when neither or both directions hold with disagreeing folds, the state is genuinely ambiguous (proven by command-built cases with byte-identical slots and opposite answers) and refuses with its own typed error, naming `prikk compact –received-index–trust-policy –keep-slot a

Provenance

This reference implements DC-31 as a documentation-only extension of the DC-24 current-state reference series. It adds no code, schema, CLI behavior, repository behavior, trust behavior, verification behavior, repair behavior, or repository-format stability guarantee.