# fx-init $ dhake -f Dhakefile.dhall rootfs

Boot

The pinned boot sequence

// mkdirs → DBs → bootlog → store facts → dhake → boot_status → main loop

Boot is a fixed, ordered sequence. fx-init mkdirs the run-dir, installs signal handlers (SIGTERM/SIGINT → shutdown, SIGCHLD → self-pipe), then holds the runtime DB (state.db) and the log DB (log.db) open for the life of the process. It reads the durable .bootlog, transiently opens the store to read the CURRENT generation's facts AS-OF that version, then execs dhake to materialize the rootfs.

1. mkdirs run-dir; signal handlers (SIGTERM/SIGINT->shutdown, SIGCHLD->self-pipe)
2. dl_open(run-dir/state.db) held for life; declare runtime + probe relations
3. fx_log_open(run-dir/log.db) held for life
4. read durable <store>/.bootlog ("<version> <status> <epoch>")
5. transient store open: current v; read gen/svc facts AS-OF v (or rolled-back v')
6. boot decision: stale in-progress/failed for v + known-good v'<v -> roll forward
7. append .bootlog (v, in-progress, now)
8. fork+exec dhake -f <buildfile> rootfs; pipe output to the log DB
9. txn: generation_current(v), boot_status(v, in-progress)
10. build on= readiness graph, validate, enter main loop

Materializing the rootfs

// fork+exec dhake on the generation's buildfile

The stored buildfile embeds the activation-time host store root in its let GEN = "<host_store>/..." and every Symlink from. At boot the store may live under a different root (chroot: /fx/store, or any --store), so fx-init rewrites that host root to its own store (fx_reloc_rewrite_buildfile) and execs dhake on the rewritten copy under the run dir. The to paths (/etc/.../bin/.../run/...) are absolute and pass through unchanged.

A failed rootfs materialization means the boot can never reach ok: /etc + /bin were not materialized. fx-init pins the decision the moment dhake exits non-zero, so the START-ONLY grace rule can never flip boot_status to ok afterward.

boot_status: START-ONLY

// ok is decided at grace-end, never before

Boot-ok is decided exactly once, at grace-end. Every service must have reached STARTED and none may have exited during the grace window — a service that crashes during boot is a start-failure, pinned failed in reap_children. A hang (a service that never starts) fails via the grace timeout. After boot-ok is decided, health regressions restart in place and never touch boot_status.

Waiting until grace-end is what closes the race: declaring ok the instant all services are STARTED would race a service that crashes a few hundred milliseconds later, still inside the window. Letting the window elapse lets reap pin any in-window exit as failed before ok can be said.

1

config-bad-exit

A crasher exits during the grace window → failed via the reap pin. The service keeps restarting per its restart policy; rollback happens on the next boot.

2

config-bad-hang

A gate never comes ready, so a service never starts → failed via the grace-timeout branch. The boot never reaches ok.

Roll-forward rollback

// monotonic: re-publish a known-good predecessor as a new version

On boot, fx-init reads the durable .bootlog. If the last boot of the CURRENT version v was in-progress or failed, and a known-good ok predecessor v' < v exists in the snapshot versions, fx-init rolls forward: it re-publishes v''s full state as a new CURRENT and reads facts AS-OF the new CURRENT. The system stays up; rollback takes effect on the next boot.

fx_store_rollback only restores fxstore's own package facts — not the M4 init relations (svc, generation, …) written by fx-activate. Those would otherwise stay as the failed boot's, so the re-published snapshot would carry the wrong service set. fx-init restores the predecessor's M4 facts into the live WAL before the rollback, so its publish captures the predecessor's full state (packages + service set).