# fx-activate $ fx-activate --config config.dhall

Activate

Build-time activation

// config.dhall → closure → Dhakefile → facts → snapshot

fx-activate is the build-time tool that turns a config.dhall into a bootable generation. It evaluates the config (dhall-c), loads the package set, computes the dependency closure, verifies every closure package is built, and requires dhake, fx-init, fxctl and fx-activate in the closure.

1. fx_packageset_load + fx_config_load(config.dhall)
2. fx_store_open
3. compute_paths (closure over config.packages)
4. verify every closure package is BUILT (store fact + stat dir)
5. resolve each service argv[0] against its pkg's store path + target
6. render the generation in memory (Dhakefile + etc/* + bin symlinks)
7. gen hash = sha256_hex over a canonical serialization
8. write + rename to <root>/<genhash>-system-generation (adopt if exists)
9. declare + txn_add_fact the generation facts; dl_publish_snapshot
10. print "activated <genhash> as version <v>; buildfile <path>"

The per-generation Dhakefile

// /etc files via Copy + Chmod, /bin via Rm+Symlink, all targets phony

The emitted Dhakefile is a small, validated template. /etc files are copied from the generation dir with Copy{from=GEN/etc/<f>} and always followed by Chmod (umask independence). Every /bin symlink is preceded by Rm<Plain> — dhake's bare symlink(2) fails on EEXIST. All targets are phony (always re-asserted).

Content-addressed generations

// canonical serialization → sha256 → adopt-if-exists

The generation hash is sha256 over a canonical serialization: a fxgen-v1 magic, the hostname, the sorted /etc files, the sorted services (with argv, pkg, on, restart, backoff, probe), and the sorted closure store paths. The same inputs always produce the same hash, so re-activation of an identical config adopts the existing generation dir instead of rebuilding it.

Store paths are recorded store-relative (<hash>-<name>, not <store_root>/<hash>-<name>) so the genhash is independent of the store root. A generation activated against a host temp dir boots identically after the store is relocated to /fx/store in the chroot.

The generation facts

// generation · svc · svc_argv · svc_bin · svc_backoff · svc_env · svc_probe · user · boot_grace

fx-activate declares the M4 init relations and writes one self-consistent set of facts per activation — clearing the previous activation's facts first, so a re-activation boots exactly this generation's service set, not the union of all past ones. boot_grace(ms) is persisted as a raw u32 so fx-init (which cannot read Dhall) honors the per-activation grace timeout from config.

How fx-init consumes these facts — at boot it reads them AS-OF the current version (or the rolled-back predecessor) and builds the in-memory service table.
The boot sequence →