cairn splits in one place: the catalog is public, the hosts are private. The catalog is the whole build path, from the image recipes to the CI components that build, gate and sign them. What stays private is the machines and the host config layered over a published variant, and no verification anyone runs depends on trusting either.

ONE SEAM PUBLIC PRIVATE PROMOTED DIGEST TRUST ANCHORS COMPONENTS pipeline build · gate · promote RECIPES base FROM scratch, Fedora CATALOG images :stable = promoted digest OUT OF BAND site /keys/ fingerprints INSTANCES hosts host config

Only a promoted digest crosses the seam, and everything needed to rebuild and verify what a host runs stays on the public side of it.

Verify before trust

Trust is anchored out of band: the key set is minted once in an offline ceremony and published with SHA-256 fingerprints at /keys/, so a verifier compares against a published fingerprint and never asks the registry to vouch for itself. Where a repo opts in, images are signed with cairn’s own cosign key, which containers/image verifies at the pull (the trust chain).

Public anchors ship, private halves never

The image carries its own Secure Boot payloads, so systemd-boot auto-enrolls them from Setup Mode, and the private halves stay in the offline ceremony (D19).

The gate tests what production boots

The tag a host installs moves only after that exact artifact installs and boots for real, under enforcing Secure Boot with no Microsoft key in the chain, on a protected runner (D27).

THE FLAGSHIP'S TAGS PASSES FAILS CANDIDATE the new image a digest, not a tag THE BOOT GATES it boots for real enforcing Secure Boot ON A PASS the tags move to the new digest ON A FAILURE the tags stay on the last good image

The tag is a claim about a boot that already happened, never a promise.

A failure starves the cascade downstream (the pipeline).

Use the upstream’s extension point

Kernel arguments, initramfs policy and the composefs switches go in the directories their upstreams publish for the purpose, never in an upstream’s own files, so a version bump does not surprise the build (D5).

Declare every divergence

Each build step in the image recipe carries a cf: block stating what it does, why, and the upstream it follows. .deviations.yaml declares each departure from the estate’s own standards with an owner and a reason, and an expiry unless the departure is permanent.

One fact, one home

The tree states a fact, this site says how cairn works, a README says what a repo is and how to run it, its docs/decisions.md says why (the rule). Anything code or CI states is linked, never restated.

Decisions are pruned, not stacked

A record earns its place only while it states something in force that the tree does not; one whose subject is gone is deleted, a reversal rewrites its own record, and git carries the history.

Lineage

cairn/base re-instantiates the proven shape of its predecessor (D9). What cairn adds is the climb that predecessor deferred while upstream was experimental (D13): a sealed composefs root under systemd-boot and unified kernel images, measured boot, own-key signing and state-bound disk encryption (sealing).