Expand description
Exporting a node root, importing one, and settling what an export claims (§E).
The plan’s standing rule for a one-way door is that no persisted
format ships without three things: a version field, a written
evolution policy, and export/import tooling. The op log has had the
first two since its first byte — every entry carries
format_version, and crate::platform documents the additive
rule. This is the third, and it is deliberately a format tool
rather than a deployment one.
That distinction is the reason this exists beside scripts/pull_backup.sh
instead of replacing it. That script is disaster recovery for one
deployment: it pulls over ssh from a fixed remote path, refuses to run
on the machine that holds the log, takes its repository list from a
policy file, needs python3 to project a hash, and checks the bundles
against the D25 attestation. All of that is right for the job it does
and none of it travels. Here there is no network, no ssh key, no
repos.list, and no interpreter: a directory goes in, a directory
comes out, and the log itself says what the export must contain.
§What an export claims, and how the claim is settled
An export is not a pile of files, it is an assertion with two halves
that can disagree: this op log and these repositories describe the
same node. verify settles it by folding the log into a
choir_view::View and requiring every ref the view names to be
present in that repository’s bundle at the same oid.
The check is deliberately one-directional. A bundle may carry refs the
log does not name; a log may not name refs no bundle carries. That
asymmetry is what makes exporting a running node meaningful: the log
is read first and the bundles after, so a push landing mid-export puts
the bundles ahead, which is a state the export can honestly describe.
The reverse — the log naming a commit no bundle holds — is the failure
that matters, because restoring it produces a node whose view points
at objects it does not have, and startup reconciliation answers that
by appending signed retractions of exactly the refs being restored.
Report::ahead counts the benign direction rather than hiding it,
so an operator can see drift instead of inferring it.
§What an export does not contain
Secrets, by construction and then by inspection. Only four names are
ever copied out of .choir, and the finished directory is walked
again afterwards and refused if it holds anything matching
is_secret — so the guarantee is a property of the output rather
than of the care taken while writing it. The node’s signing key stays
with the node that owns it, and docs/runbook-restore.md covers what
its absence means.
Policy files are also absent, and that is not an oversight worth
quietly tolerating: --acl-file, --auth-file, --keys-file and the
rest name paths anywhere on the host, so a root does not know where
they are and an export taken from one cannot honestly claim to hold
them. The manifest says so in a field rather than leaving the reader
to notice. Provisioned workspaces and checkouts under .choir are
left behind too, being derived from refs the export does carry.
Structs§
- Report
- What an export holds, counted from the files rather than claimed.
Constants§
- FORMAT_
VERSION - Version of the export directory’s own layout, carried in the manifest.
Functions§
- export
- Writes an export of
rootintodest, then verifies its own output. - import
- Places the export at
dirinto a fresh noderoot. - is_
secret - Whether a file name is one an export must never carry.
- repos
- Bare repositories under
root, asowner/name.gitpaths. - verify
- Settles what an export at
dirclaims: the log verifies, the manifest describes it, and every ref the log names is in a bundle at that oid.