Expand description
Phase 35 — the config time machine (ADR-0141).
Config lives in git and its secrets live elsewhere, and the two drift: the copy in git is redacted, so it is not the file that runs, and the file that runs is versioned by nobody. The vault is the one place that holds both, so it keeps a snapshot of every rendered config whenever that config changes.
§Two copies of each snapshot, on purpose
content is the file exactly as it would be deployed, secrets included.
masked is the same render with every resolved secret replaced by its
fingerprint, which is what unv project export already prints to a
terminal. Every default view (list, show, diff, the app) reads masked, so
“this key changed” is visible without the key ever being read; content
is reached only with --reveal/--out or the app’s Reveal. Masking the
stored text afterwards would not work: a rotated-away secret is no longer in
the vault, so nothing would know to mask it in an old snapshot.
§Tamper evidence and retention
Each stream (project and exporter) is a hash chain: chain = sha256(prev ‖ project ‖ exporter ‖ sha(content) ‖ sha(masked) ‖ at). Old secrets living
forever is a liability, so history is pruned by policy (keep newest and
everything younger than days), and pruning a prefix would normally break
the chain. A checkpoint row records the chain value at the pruned
boundary so the remainder still verifies, and the prune also appends a row
to the vault’s own audit chain naming that value; verify requires the two
to agree, so forging a checkpoint means forging the audit chain too.
Structs§
- Policy
- Prune
Report - Snap
Meta - Snapshot
- Stats
- Stream
- One rendered stream: which project and which exporter produced the text.
- Verify
Report
Constants§
- DEFAULT_
DAYS - DEFAULT_
KEEP - MAX_
BYTES - A snapshot larger than this is not stored. A rendered config is kilobytes; anything bigger is a file this feature was not built for.
Functions§
- diff
- A unified diff between two snapshots of the same stream.
maskedpicks the fingerprinted text (the default everywhere) or the real one. - exposed_
by_ sha - What the files with this hash contained: the union over every snapshot that
recorded it (the same text can be recorded again after a revert), or
Nonewhen no snapshot has it, so a caller can tell “nothing exposed” from “unknown”. - find_
by_ sha - Snapshots whose content has this hash, newest first. This is how a hub says “the file on that host is the one rendered on 3 October”.
- get
- init_
schema - latest
- The newest snapshot of a stream.
- list
- policy
- prune
- Deletes, per stream, every snapshot that is both beyond the newest
keepand older thandaysdays, leaving a checkpoint so the rest still verifies.dry_runcounts without touching anything. - record
- Records a snapshot when
contentdiffers from the stream’s latest. Returns the new sequence number, orNonewhen nothing changed (or history is off). - resolve_
pair - Picks the two snapshots a diff should compare. With
from/tounset, the two newest of the stream named byproject(andexporter, which is required when the project has more than one stream, such as Compose and its.env). - set_
policy - stats
- verify
- Recomputes every hash and every chain, and checks each checkpoint against the audit chain.