Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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, gated zpool upgrade shim, 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: signed curl|sh from packages.osterman.co first, then apk/deb/overlay; keyboot/keyboot-boot/keyboot-install split; ESP staged-not-committed on upgrade. accepted. (ci/install.sh) Boot-package post-install refined to auto-promote by 0009.
  • 0009 — keyboot self-upgrade: A/B ESP slots with boot-count recovery (trial slot + keyboot.env grubenv; 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 form mirror 2 or explicit mirror 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 caps zfs_arc_max to 512 MiB (uniform; it only RO-imports) with a keyboot.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-label overrides); 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 points keyboot.mode=memtest
    • stage-8 m (kexec, best-effort/unverified) + a GRUB 11_keyboot_memtest chainload seam (reliable). accepted. (init/lib/memtest.sh, keyboot-install install --memtest)
  • 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 via ci/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 ESP KEYBOOT - RAM RESCUE entry + ci/stage-ramrescue.sh, qemu:ramrescue green).
  • 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 binary authenticatorData (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), then kexec the 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.