docs/decisions/
Architecture Decision Records. One file per decision, immutable once merged; supersede an old ADR with a new one rather than editing.
Filename format: NNNN-slug.md, zero-padded sequence, kebab-case slug.
Example: 0001-bash-not-ash-in-init.md.
Each ADR documents: context, decision, alternatives considered, consequences. Keep them short — link to SPEC sections rather than restating them.
Index
0001— MIT license.0002— BE re-unlock passphrase over SSH (dropbear in the BE). Superseded by 0003 (retained as the no-handoff fallback path).0003— BE re-unlock by handing the keyfile payload across kexec (single prompt). Reverses SPEC §16.1/§17.1 to choice (b).0004— OpenZFS version & pool-feature lifecycle (keyboot is the ZFS floor):compatibility=pin, gatedzpool upgradeshim, boot-time feature check, ESP read-only by default. accepted.0005— Dataset layout & boot-environment scope:/var+pkgdb stay in the BE; BE-scoped vs persistent datasets; the two-homes rule. accepted.0006— Snapshot naming (@<UTC>Z-<LABEL>), cross-distro autosnapshot tool, and TZ surfacing (BE carries a TZ; keyboot bundles tzdata). accepted.0007— BE rollback model: three rungs (read-only / clone / destroy), pre-boot, recursive set, dependent-clone guard. accepted.0008— Packaging & delivery: signedcurl|shfrompackages.osterman.cofirst, then apk/deb/overlay;keyboot/keyboot-boot/keyboot-installsplit; ESP staged-not-committed on upgrade. accepted. (ci/install.sh) Boot-package post-install refined to auto-promote by0009.0009— keyboot self-upgrade: A/B ESP slots with boot-count recovery (trial slot +keyboot.envgrubenv; keyboot commits on unlock+import; auto-revert to the known-good slot on failure). Closes 0004’s keyboot-self-upgrade open boundary. accepted.0010— Multi-disk vdev topology grammar (data + special/log/cache/spare): count formmirror 2or explicitmirror sn-a sn-b; per-disk partition role (data/spare bootable, aux crypt-only); warn+confirm on no-redundancy. accepted. (keyboot-install topology plan)0011— ZFS module params: keyboot’s unlock env capszfs_arc_maxto 512 MiB (uniform; it only RO-imports) with akeyboot.zfs_arc_max=override; the booted OS owns production ARC (Ansible). accepted. (init/lib/zfs.sh)0012— Boot-environment naming:<pool>/ROOT/<stem>-<YYYY-MM-DD-HHMM>Z(UTC, ADR 0006 stamp); stem defaults to distro (--be-labeloverrides); upgrade/rollback re-stamp the stem (no stacking); provenance in user props. accepted. Staged: helper + upgrade stem-strip now, install-default flip follow-up. (keyboot-install-os/lib/benaming.sh)0013— Automation keyslot: slot 2 is the canonical, independently-revocable automation passphrase for unattended/Ansible unlock (keyfile enroll-automation); Ansible feeds it to the existing dropbear askpass path. accepted. (keyfile enroll-automation,ansible/keyboot_unlock/)0014— Bootable memtest86+: ESP-staged; entry pointskeyboot.mode=memtest- stage-8
m(kexec, best-effort/unverified) + a GRUB11_keyboot_memtestchainload seam (reliable). accepted. (init/lib/memtest.sh,keyboot-install install --memtest)
- stage-8
0015— Ansible roles:keyboot_unlock(done) +keyboot_substrate(rescue→install) +keyboot_deploy(clone→boot-once→health-gate→promote, revert via boot-once not the role). Health gate = an operator command. accepted. (ansible/keyboot_{substrate,deploy}/)0016— Web UI for unlock + BE selection: additive/opt-in, never replaces dropbear. Trust = bake a self-signed TLS cert like the SSH host keys + import once (browser then auto-detects MITM), console-fingerprint as the bootstrap fallback (browsers can’t expose TLS channel-binding to JS, so no in-page MITM proof). Phase 1 = localhost-only +ssh -L(SSH-grade security); Phase 2 = network-exposed mTLS/passkey, opt-in. Rust+rustls, same unlock code. proposed / DRAFT.0017— RAM rescue (keyboot.mode=ramrescue): kexec a SEPARATE, fuller rescue image (real coreutils/parted/lvm/smartctl + zfs + cryptsetup + keyboot-install + the install-os orchestrator) entirely into RAM — keyboot’s “own env IS the rescue” vision, full and disk-independent. Built viaci/build-image.sh RESCUE_FULL=yes; staged on the ESP (like memtest) or netbooted; reuses the memtest kexec seam. Normal boot stays small; the big rescue loads on demand. accepted — BUILT (v-next;keyboot.mode=ramrescue,RESCUE_FULL=yes, the ESPKEYBOOT - RAM RESCUEentry +ci/stage-ramrescue.sh,qemu:ramrescuegreen).0018— Passwordless WebAuthn-PRF unlock (ADR 0016 Phase 3): touch a hardware authenticator in the browser → its PRF (FIDO2 hmac-secret) output is a reserved LUKS keyslot → the pool unlocks. No SSH, no typed passphrase, no cert files; phishing-resistant + hardware-backed. Boot-path verify =ring(P-256 + SHA-256) over a plain binaryauthenticatorData(no CBOR at boot); COSE pubkey extracted + baked at enrollment. Over the Phase-2 baked-TLS channel. proposed.0019— Off-disk rescue verification (TPM-free measured launch): on an unexpected reboot, verify the on-disk keyboot from an INDEPENDENTLY-sourced rescue (Hetzner’s off-disk rescue) against an OPERATOR-signed manifest (pinned key supplied out-of-band), thenkexecthe verified bytes directly (no reboot ⇒ no TOCTOU). The TPM-free substitute for measured boot when there’s no TPM/SB/ netboot-control; defeats file-rewrite tamper, residual = a fully-malicious provider rescue (bigger lift). Tool:tools/keyboot-verify. proposed/BUILT (verifier).
Backlog: the design Q&A from 2026-05-26 captured in SPEC §3 (Decision Log) should be backfilled here as individual ADRs so the rationale survives independent of SPEC revisions.