Firn installs snosi bootc OCI images from a declarative recipe, with a built-in terminal wizard and progress protocol.
Firn is a GPL-3.0-only, deeply-inspired rewrite of
fisherman. Attribution to the code it draws
from lives in NOTICE; the rationale for rewriting rather than
forking is ADR-0003.
- Bootc-only installer.
family = "bootc"installs an OCI image straight from a container registry (deployed from the RAM installer over containers-storage, ADR-0012). - Recipe-driven. A single versioned TOML file (schema, ADR-0005) describes the target disk, image, security options, and system configuration. Validation is fail-closed: unknown fields, unknown enum values, and wrong-family fields are errors, never warnings. New recipes use version 2; bootc version 1 is temporarily accepted with a deprecation warning; A/B version 1 is rejected.
- Security options. LUKS root with passphrase or TPM2 auto-unlock; UEFI Secure Boot with MOK enrollment via secure-install schema-1 (ADR-0014).
- Built-in TUI. The bare
firncommand opens a terminal wizard (single binary, Charm stack, ADR-0007) that walks the install and emits a recipe you can reproduce headlessly. - One progress protocol. Every user-visible signal from the pipeline is a
typed progress event (spec);
--json-progressstreams it as NDJSON for automation.
firn # open the interactive wizard
firn validate recipe.toml # check a recipe against the schema + this machine
firn install --confirm /dev/nvme0n1 recipe.toml # install; confirmation must exactly match [target].disk
firn install --dry-run recipe.toml # preflight only; no confirmation required
firn install --json-progress --confirm /dev/nvme0n1 recipe.toml # install with NDJSON progressNon-dry-run headless installs are destructive and require
--confirm <target-disk>, where the value exactly matches the recipe's
[target].disk. A headless --dry-run does not modify disks and does not
require confirmation. --dry-run is supported only as the command-local
firn install --dry-run <recipe.toml> form: the inherited global flag is
rejected for bare firn before the wizard starts and for firn validate
before validation, with an error directing the reader to the supported
recipe-backed preflight command.
firn install takes detection overrides (--secure-boot, --tpm, and
--uefi, each auto|on|off) that force the machine-capability probes when you
know better than autodetection — e.g. in a VM. They also reach the wizard when
you run firn install with no recipe; the bare firn command always
autodetects. --uefi is install-only (firn validate accepts
--secure-boot and --tpm but not --uefi, which it does not consume).
See docs/specs/recipe-schema.md
for a complete recipe.
Firn is a single Go binary with no CGO. The pipeline and domain packages use only the Go standard library plus host tools it shells out to; the sanctioned dependencies are a TOML decoder and, in the TUI layer only, the Charm stack (ADR-0011).
make build # build ./build/firn
make check # fmt + lint + test — the gate for "done"
make ci # credential-free gate mirroring CI: verify, coverage, race
# detector, and linux amd64/arm64 buildsEnd-to-end harnesses under test/ install into throwaway QEMU guests
and verify the booted disk over SSH (test/e2e-bootc.sh,
test/e2e-bootc-secure.sh, test/e2e-tui.sh).
Firn's own contracts are the recipe schema and the progress protocol, not a Go
API, so the pipeline lives under internal/:
cmd/firn-cli/ clix entry point (main)
cmd/firn/ cobra command handlers (install, validate, TUI)
internal/recipe/ TOML recipe model + fail-closed validation
internal/steps/ bootc pipeline assembly + option splices
internal/pipeline/ the step engine, cleanup stack, runner seam
internal/bootcimg/ bootc install (podman/bootc)
internal/enroll/ MOK and TPM enrollment helpers
internal/secureboot/ UEFI Secure Boot ESP chain + contract (schema-1)
internal/tui/ the terminal wizard
internal/progress/ the progress protocol emitters
Start at docs/README.md. Docs are split by the question
they answer: adr/ (why — immutable decisions), design/
(how — living architecture), specs/ (what exactly — testable
contracts), plans/ (when — phased roadmap). Every doc starts
from its category's TEMPLATE.md and cross-links the docs it shapes.
Agent instructions are canonical in AGENTS.md; CLAUDE.md,
GEMINI.md, and .github/copilot-instructions.md are symlinks to it, and
.claude/skills symlinks to .agents/skills/
(ADR-0002) — every agent
reads the same law.