Boot URL, typed at the board’s HTTP Boot setting:

https://cairn.dunn.dev/ipxe-shim.efi
http://cairn.dunn.dev/ipxe-shim.efi     # a board without HTTPS boot

Every cairn host installs the same way: boot a live environment under Secure Boot, then run one helper against one universal Ignition at https://cairn.dunn.dev/ignition.

1. Boot the live environment

Secure Boot stays on, under the board’s factory keys, through this step.

Network (primary). Type the boot URL above at the board. It loads ipxe.efi beside it: iPXE v2.0.0’s release, unmodified and signature-checked before it runs, so plain http suits these two alone. iPXE then reads autoexec.ipxe there, fetches the FCOS kernel and initramfs over https, and boots through Fedora’s signed shim.

USB stick (fallback). media-check verifies both install ISOs in CI and publishes each one’s URL and sha256. Play it on main, then fetch, check, and burn Stick A:

mkdir -p ~/cairn-media && cd ~/cairn-media
curl -fLO "$(cat fcos-live.url)" && shasum -a 256 -c fcos-live.sha256
diskutil unmountDisk /dev/diskN
sudo dd if=<iso> of=/dev/rdiskN bs=4m
sudo cmp -n "$(stat -f%z <iso>)" <iso> /dev/diskN

Boot the host off it, press e at the menu, and append ignition.config.url=https://cairn.dunn.dev/ignition. (Stick B, Fedora Workstation, is a rescue stick only.)

ONE INSTALL PATH STEP 1 choose image :stable-sealed STEP 2 install to-disk tpm2-luks STEP 3 first boot TPM2-only header STEP 4 passphrase add it first POSTURE bind-tpm signed-PCR11 slot POSTURE password-only retire the TPM slot POSTURE skip keep the install binding

All three postures wait behind the passphrase.

2. Choose an image

Only sealed tags install to disk: a bare install of an unsealed tag finishes with loader entries and nothing to load them. Those tags are what instances build on (D27). The example installs base:stable-sealed; your host installs its repo’s sealed tag (running it).

3. Install

Resolve the tag to a digest, then hand both to the helper:

REPO=registry.gitlab.com/dunn.dev/cairn/base/base
DIGEST=$(skopeo inspect --no-tags "docker://$REPO:stable-sealed" | jq -r .Digest)
cairn-install "$REPO@$DIGEST" "$REPO:stable-sealed" /dev/disk/by-id/...

cairn-install logs in, pulls the digest, re-checks it against the tag, has you type the disk path back, then runs only this:

sudo podman run --rm --privileged --pid=host \
  --security-opt label=type:unconfined_t \
  -v /var/lib/containers:/var/lib/containers -v /dev:/dev \
  "$REPO@$DIGEST" \
  bootc install to-disk \
    --composefs-backend --filesystem ext4 --wipe \
    --block-setup tpm2-luks --generic-image --skip-fetch-check \
    --source-imgref "containers-storage:$REPO@$DIGEST" \
    --target-imgref "$REPO:stable-sealed" \
    /dev/disk/by-id/...

--composefs-backend lays down the verity-enforced composefs the sealed root needs (sealing); --block-setup tpm2-luks binds the LUKS2 root to the TPM instead of the unencrypted direct default. The supported bake is this install run --via-loopback, not bootc-image-builder (running it).

Enrolment on metal is manual, by design: loader.conf keeps systemd-boot’s default if-safe, which enrols only in a VM. Add secure-boot-enroll force in this session, or pick the enrol entry in Setup Mode.

Only now enter Setup Mode: delete the Platform Key in firmware setup and reboot.

4. First boot

The installed system boots in Setup Mode and enrolls our own keys, through the force line or the menu entry from step 3 (D19), off the TPM2 slot bootc install enrolled. A fresh tpm2-luks install runs systemd-cryptenroll --wipe-slot=all, so that slot is the only one the header holds. --generic-image writes no firmware boot entry, so it boots via the \EFI\BOOT\BOOTX64.EFI fallback or a hand-added efibootmgr entry.

5. Recovery passphrase, then a posture

Add a recovery passphrase first: it is the only door back in if the TPM path breaks. Then pick a posture.

  • bind-tpm: swap the plain TPM binding for the signed-PCR11 policy sealing is built around, the posture the edge lane shows.
  • password-only: retire the TPM keyslot and unlock by passphrase every boot, as the attended lane does.
  • skip: keep the binding bootc install set up.

docs/tpm-rebind.md carries the command-exact procedure for all three, run by hand as a documented operator step by deliberate choice (D14).