Skip to main content

Module portable

Module portable 

Source
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 root into dest, then verifies its own output.
import
Places the export at dir into a fresh node root.
is_secret
Whether a file name is one an export must never carry.
repos
Bare repositories under root, as owner/name.git paths.
verify
Settles what an export at dir claims: the log verifies, the manifest describes it, and every ref the log names is in a bundle at that oid.