Files
fruix/docs/reports/phase19-generation-layout-freebsd.md

6.1 KiB

Phase 19.2: explicit installed-system generation layout on FreeBSD

Date: 2026-04-04

Goal

Phase 19.2 is about making Fruix's installed-system generation model more explicit.

The target here is not yet a full Guix-equivalent in-place switch-generation workflow.

The immediate goal is to stop relying mainly on harness knowledge and implicit symlink expectations by recording installed deployment state more explicitly on disk.

Decision

Fruix now follows this design direction:

  • keep Guix-like semantics
  • do not mirror Guix's installed-system/profile layout mechanically 1:1

What Fruix preserves from Guix:

  • immutable closure identity
  • rollback-friendly deployment semantics
  • explicit current deployment pointer
  • GC-root-style retention links
  • /run/current-system as the active runtime boundary

What Fruix intentionally changes:

  • installed-system generation state is represented as a small metadata-bearing directory
  • the generation model is recorded under a Fruix-native path
  • deployment metadata and provenance are easier to inspect directly without reconstructing intent from symlink layout alone

Implemented layout

Installed systems now record an explicit generation root under:

  • /var/lib/fruix/system

Current validated initial layout:

/var/lib/fruix/system/
  current -> generations/1
  current-generation
  generations/
    1/
      closure -> /frx/store/...-fruix-system-...
      metadata.scm
      provenance.scm
      install.scm

Installed systems now also create explicit retention roots under:

  • /frx/var/fruix/gcroots

Current validated initial layout:

/frx/var/fruix/gcroots/
  current-system -> /frx/store/...-fruix-system-...
  system-1 -> /frx/store/...-fruix-system-...

Important compatibility point:

  • /run/current-system still points directly at the active closure in /frx/store

That means the new explicit generation model strengthens deployment metadata without changing the already-validated runtime contract used by activation, rc.d integration, and service startup.

Code changes

modules/fruix/system/freebsd/media.scm

Added explicit generation-layout helpers:

  • generation metadata object writer
  • generation provenance object writer
  • generation layout population for staged rootfs trees

The system rootfs staging path now creates explicit generation state during rootfs population.

That affects:

  • direct rootfs materialization
  • direct image materialization
  • direct installation targets
  • target rootfs payloads staged inside installer images
  • target rootfs payloads staged inside installer ISOs

The direct install path now also refreshes the generation layout after writing:

  • /var/lib/fruix/install.scm

so the generation directory carries the same install metadata.

Installer runtime path

The generated installer runtime script now also copies install metadata into:

  • /var/lib/fruix/system/generations/1/install.scm

on the installed target.

This keeps direct-install and installer-mediated installs aligned.

New validation harness

Added:

  • tests/system/run-phase19-generation-layout-qemu.sh

This harness builds on the already-passing direct install validation from Phase 18.1 and then verifies the new explicit generation layout on the installed target image.

Passing validation:

  • PASS phase19-generation-layout-qemu

Validated result summary:

closure_path=/frx/store/882fb4a9fbb05f08e77de29f70ca50f3c01dd29141e72688d32770a3172747e7-fruix-system-fruix-freebsd
current_generation=1
current_link=generations/1
generation_closure=/frx/store/882fb4a9fbb05f08e77de29f70ca50f3c01dd29141e72688d32770a3172747e7-fruix-system-fruix-freebsd
gcroot_current=/frx/store/882fb4a9fbb05f08e77de29f70ca50f3c01dd29141e72688d32770a3172747e7-fruix-system-fruix-freebsd
gcroot_generation=/frx/store/882fb4a9fbb05f08e77de29f70ca50f3c01dd29141e72688d32770a3172747e7-fruix-system-fruix-freebsd
run_current_system_target=/frx/store/882fb4a9fbb05f08e77de29f70ca50f3c01dd29141e72688d32770a3172747e7-fruix-system-fruix-freebsd
generation_layout=explicit
generation_layout_validation=ok

The harness verified all of the following:

  1. the installed target contains:
    • /var/lib/fruix/system
  2. the current generation pointer exists and resolves to:
    • generations/1
  3. the generation directory contains:
    • closure
    • metadata.scm
    • provenance.scm
    • install.scm
  4. the generation closure link points at the installed closure in /frx/store
  5. the generation metadata records:
    • closure path
    • install metadata path
  6. the generation install metadata records:
    • closure path
    • materialized source provenance
  7. explicit retention roots exist under:
    • /frx/var/fruix/gcroots/current-system
    • /frx/var/fruix/gcroots/system-1
  8. those GC-root-style links point at the same active closure
  9. /run/current-system still points directly at the active closure path
  10. existing install boot validation remains intact

Relationship to Guix

Fruix now has an explicit installed-system generation model, but it is still intentionally not a byte-for-byte clone of Guix's on-disk conventions.

The design choice is:

  • preserve Guix's deployment semantics
  • use a Fruix-native metadata-oriented representation where that improves clarity for operators and debugging

That decision is documented separately in:

  • docs/GUIX_DIFFERENCES.md

Current limitations

This phase does not yet add:

  • multi-generation switching in place
  • a switch/reconfigure command
  • an operator-facing rollback command that flips from current to a previous installed generation without redeploy
  • explicit rollback link management beyond the initial current-generation layout

Those belong to later Phase 19 work.

Conclusion

Phase 19.2 is complete.

Fruix now has a clearer, explicit installed-system generation and retention-root model on FreeBSD:

  • generation metadata is recorded under /var/lib/fruix/system
  • retention links are recorded under /frx/var/fruix/gcroots
  • /run/current-system remains stable as the runtime boundary
  • and the model is documented in Fruix-native terms for Guix-familiar operators