Files
fruix/docs/PROGRESS.md

3890 lines
183 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Progress
## 2026-04-03 — Phase 16.3 completed: native FreeBSD base builds now consume materialized source inputs
Completed work:
- refactored native FreeBSD package materialization in `modules/fruix/system/freebsd.scm` so native packages now:
- reconstruct the declared `freebsd-source` from the package plan
- materialize that source under Fruix control
- rewrite the native build plan to use the materialized source root
- add the materialized source store path to package references
- native build manifests now include:
- `declared-source`
- `materialized-source`
- native `.freebsd-native-build-info.scm` now records:
- declared source
- materialized source store path
- materialized source root
- materialized source tree sha256
- effective source details such as resolved Git commit / archive sha256
- native build common manifests now reuse the materialized source tree hash when available
- package materialization caching is now keyed by manifest identity instead of only package name/version, so distinct source-driven variants do not collide in-process
- system closure materialization now keeps a dedicated source-materialization cache and records:
- `metadata/freebsd-source-materializations.scm`
- `materialized-source-store-count`
- `materialized-source-stores`
- system closure references now include the materialized source stores explicitly
- `scripts/fruix.scm` now emits for `fruix system build` and `image`:
- `freebsd_source_materializations_file`
- `materialized_source_store_count`
- `materialized_source_stores`
- added validation artifacts:
- `tests/system/phase16-git-materialized-source-operating-system.scm.in`
- `tests/system/run-phase16-source-driven-native-build.sh`
- wrote:
- `docs/reports/phase16-source-driven-native-builds-freebsd.md`
Validation:
- `PASS phase16-source-driven-native-build`
- `PASS phase16-source-materialization`
- `PASS phase16-declarative-source-build`
- validated a full native system build from declared Git source:
- `https://git.FreeBSD.org/src.git`
- ref: `stable/15`
- resolved commit during validation:
- `332708a606f6bf0841c1d4a74c0d067f5640fe89`
- intentionally declared an unused transitional source-root:
- `/var/empty/fruix-unused-source-root`
and confirmed native build info instead used the materialized source root under `/frx/store/*-freebsd-source-*/tree`
Current assessment:
- Phase 16 is now fully complete
- Fruix can:
- declare FreeBSD source inputs
- materialize them under `/frx/store`
- build native FreeBSD base artifacts from those materialized source snapshots
- the next step is Phase 17:
- build and compare side-by-side source revisions, then boot from them
## 2026-04-03 — Phase 16.2 completed: Fruix now materializes FreeBSD source inputs under its control
Completed work:
- added a new exported source materializer in `modules/fruix/system/freebsd.scm`:
- `materialize-freebsd-source`
- added cache-backed materialization for source kinds:
- `local-tree`
- `git`
- `src-txz`
- added cache locations under:
- `/frx/var/cache/fruix/freebsd-source/git`
- `/frx/var/cache/fruix/freebsd-source/archives`
- materialized source outputs now live in `/frx/store` as:
- `*-freebsd-source-*`
- each materialized source now records:
- declared source
- effective/resolved source
- source store path
- effective source root
- source tree sha256
- cache path
- added automatic effective-root detection so archive-backed sources that unpack as `usr/src/...` are still usable later:
- Git exports use `.../tree`
- `src.txz` archives use `.../tree/usr/src`
- added a new user-facing CLI path in `scripts/fruix.scm`:
- `fruix source materialize SOURCE-FILE`
- new source command options:
- `--source NAME`
- `--store DIR`
- `--cache DIR`
- `--help`
- source CLI now emits machine-readable metadata for:
- declared source fields
- materialized store path/root
- source tree hash
- cache path
- resolved Git commit
- verified archive sha256
- tightened `src-txz` validation so materialization now requires:
- URL
- sha256
- added validation artifacts:
- `tests/system/phase16-git-freebsd-source.scm.in`
- `tests/system/phase16-txz-freebsd-source.scm.in`
- `tests/system/run-phase16-source-materialization.sh`
- wrote:
- `docs/reports/phase16-source-materialization-freebsd.md`
Validation:
- `PASS phase16-source-materialization`
- `PASS phase16-declarative-source-build`
- verified Git source fetch/materialization from:
- `https://git.FreeBSD.org/src.git`
- ref: `stable/15`
- resolved commit during validation:
- `332708a606f6bf0841c1d4a74c0d067f5640fe89`
- verified canonical release archive fetch/materialization from:
- `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz`
- sha256:
- `83c3e8157b6d7afcae57167fda75693bf1e5f581ca149a6ecb2d398b71bdfab0`
- verified repeated materialization returns stable store paths for both the Git and `src.txz` cases
Current assessment:
- Phase 16.2 is complete
- Fruix can now fetch or materialize declared FreeBSD source trees into `/frx/store` with cache-backed provenance under `/frx/var/cache/fruix/freebsd-source`
- the next step is Phase 16.3:
- teach native FreeBSD kernel/world/runtime builds to consume these materialized source artifacts instead of ambient `/usr/src`
## 2026-04-03 — Phase 16.1 completed: FreeBSD source inputs are now explicit Fruix objects
Completed work:
- added `docs/PLAN_4.md` to define the post-Phase-15 roadmap around:
- declarative FreeBSD source acquisition
- installation artifacts
- the controlled path toward self-hosting
- introduced a first-class `freebsd-source` record in `modules/fruix/packages/freebsd.scm` with:
- supported kinds:
- `local-tree`
- `git`
- `src-txz`
- exported accessors for:
- `name`
- `kind`
- `url`
- `path`
- `ref`
- `commit`
- `sha256`
- new `%default-freebsd-source`
- extended `freebsd-base` so it now records both:
- transitional `source-root`
- declarative `source`
- added/exported:
- `freebsd-base-source`
- threaded declared source fields into native package plans so native build outputs can record them
- in `modules/fruix/system/freebsd.scm`:
- added source validation for:
- `local-tree`
- `git`
- `src-txz`
- added `freebsd-source-spec`
- `freebsd-base-spec` now nests the declared source
- native manifests and `.freebsd-native-build-info.scm` now include:
- `declared-source`
- closures now generate:
- `metadata/freebsd-source.scm`
- `metadata/store-layout.scm` now records:
- `freebsd-source`
- in `scripts/fruix.scm`, `build` and `image` metadata now emit:
- `freebsd_source_name`
- `freebsd_source_kind`
- `freebsd_source_url`
- `freebsd_source_path`
- `freebsd_source_ref`
- `freebsd_source_commit`
- `freebsd_source_sha256`
- `freebsd_source_file`
- added validation artifacts:
- `tests/system/phase16-declarative-source-operating-system.scm.in`
- `tests/system/run-phase16-declarative-source-build.sh`
- `tests/system/validate-phase16-freebsd-source.scm`
- compared Fruix's new source model with Guix's source modeling via:
- `~/repos/guix/guix/packages.scm`
- `~/repos/guix/guix/git-download.scm`
- wrote:
- `docs/reports/phase16-declarative-source-model-freebsd.md`
Validation:
- `PASS phase16-declarative-source-build`
- source model probe confirmed support for:
- local-tree `/usr/src`
- Git refs such as `stable/15` at `https://git.FreeBSD.org/src.git`
- canonical `src.txz` URLs such as:
- `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz`
- `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz`
- closure/native metadata now records the declared source explicitly while preserving the current validated `/usr/src` build path
Current assessment:
- Phase 16.1 is complete
- Fruix can now describe FreeBSD source inputs explicitly, but it does not fetch/materialize them yet
- the next step is Phase 16.2:
- fetch or materialize declared FreeBSD source inputs under Fruix control and use their stable identity as the next reproducibility boundary
## 2026-04-01 — Phase 1.1 started: Guile verified on FreeBSD amd64
Completed work:
- installed/confirmed `guile3-3.0.10`
- added a reusable verification harness:
- `tests/guile/run-phase1-verification.sh`
- `tests/guile/verify-phase1.scm`
- `tests/guile/modules/phase1/sample.scm`
- verified the following on `FreeBSD 15.0-STABLE` amd64:
- module loading
- deterministic output generation
- file I/O
- process handling with `primitive-fork`/`waitpid`
- loopback TCP sockets
- FFI calls into libc
- execution of Guix bootstrap-related code from `(guix build make-bootstrap)`
- wrote the results to `docs/reports/phase1-guile-freebsd.md`
Notable findings:
- `guile3` and `guile-3.0` are present, but there is no unversioned `guile` binary
- `system*` and `open-pipe*` currently segfault on this host (`exit 139`)
- despite that crash, the lower-level process primitives needed for further investigation do work
Current assessment:
- Phase 1.1 has a solid amd64 smoke-verification baseline
- Phase 1.1 is not fully complete yet because `i386` has not been checked and the subprocess crash needs investigation
- verification harness committed as `e380e88` (`Add FreeBSD Guile verification harness`)
## 2026-04-01 — Phase 1.1 follow-up: subprocess crash isolated
Completed work:
- added a dedicated subprocess diagnostic harness:
- `tests/guile/run-subprocess-diagnostics.sh`
- `tests/guile/posix-spawn-freebsd-diagnostics.c`
- reproduced crashes for:
- `system*`
- `spawn`
- `open-pipe*`
- confirmed all three fail with `SIGSEGV` / `exit 139`
- confirmed native FreeBSD `posix_spawn` + `posix_spawn_file_actions_addclosefrom_np` works in a standalone C program
- confirmed FreeBSD behavior that triggers gnulib replacement logic:
- `posix_spawn_file_actions_adddup2` accepts an invalid fd in the gnulib probe
- `posix_spawn_file_actions_addopen` accepts an invalid fd in the gnulib probe
- `posix_spawnp` accepts a shebang-less executable script in the gnulib security probe
- wrote the analysis to `docs/reports/phase1-guile-subprocess-crash.md`
Conclusion:
- this is most likely an upstream Guile/gnulib ABI bug on FreeBSD, not a Guix-specific problem
- likely sequence:
1. gnulib enables `REPLACE_POSIX_SPAWN=1`
2. Guile still enables `HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP`
3. Guile passes a gnulib replacement `posix_spawn_file_actions_t` object to native `posix_spawn_file_actions_addclosefrom_np`
4. libc interprets gnulib struct fields as a native pointer and crashes
- evidence from the lldb core matches this hypothesis (`*fa = 0x0000000600000008`, consistent with gnulib `_allocated=8`, `_used=6`)
Current assessment:
- Phase 1.1 amd64 investigation is now much stronger and has a concrete root-cause hypothesis
- the next practical step is to validate a workaround or patch in Guile so subprocess helpers stop crashing
- after that, continue with Phase 1.2 (minimal native build environment / GNU Hello)
## 2026-04-01 — Phase 1.1 follow-up: local Guile build validated the fix
Completed work:
- installed the additional build tooling needed for a local Guile checkout build:
- `autoconf`
- `automake`
- `libtool`
- `gettext-tools`
- `texinfo`
- `help2man`
- `gperf`
- `pkgconf`
- confirmed a FreeBSD-specific bootstrap quirk:
- Guile `autogen.sh` needs GNU `m4`
- FreeBSD base `/usr/bin/m4` is not sufficient
- `M4=gm4 ./autogen.sh` works
- built a disposable validation copy from `~/repos/guile`
- confirmed `~/repos/guile` already contains upstream commit:
- `eb828801f621d3e130b6fe88cfc4acaa69b98a03`
- `Don't use posix_spawn_file_actions_addclosefrom_np with glib posix_spawn`
- updated the local test harnesses so they can test non-system Guile builds:
- `tests/guile/run-phase1-verification.sh`
- `tests/guile/run-subprocess-diagnostics.sh`
- both now accept `GUILE_BIN`
- both now prepend the sibling `../lib` directory to `LD_LIBRARY_PATH` when a matching local `libguile-3.0.so.1` exists
- subprocess diagnostics now supports `EXPECT_GUILE_SUBPROCESS_CRASH=0` for fixed builds
- validated that the packaged Guile still reproduces the crash
- validated that the locally built Guile succeeds for:
- `system*`
- `spawn`
- `open-pipe*`
- re-ran the broader Phase 1.1 Scheme verification suite successfully against the local Guile build
- wrote the results to `docs/reports/phase1-guile-local-build-validation.md`
Important findings:
- the local Guile executable initially still crashed until it was forced to load its matching local `libguile-3.0.so.1`
- once `LD_LIBRARY_PATH` pointed at the local install lib directory, subprocess helpers worked correctly
- this strongly supports the earlier diagnosis and shows that the upstream Guile fix resolves the problem in practice
- the local `~/repos/bdwgc` checkout was not needed for this step; packaged `boehm-gc-threaded` was sufficient so far
Current assessment:
- Phase 1.1 now has both a root-cause analysis and a working validated fix path on amd64
- no source changes were needed in `~/repos/guile` because the local checkout already contains the relevant upstream fix
- no source changes were needed in `~/repos/bdwgc` yet, but the earlier FreeBSD warning keeps it on the watch list
- the project can now move on to Phase 1.2 with a known-good local Guile fallback
## 2026-04-01 — Phase 1.2 started: native GNU Hello build validated on FreeBSD
Completed work:
- added a reusable native build harness:
- `tests/native-build/run-gnu-hello.sh`
- used the current Guix package definition in `~/repos/guix/gnu/packages/base.scm` as the source of truth for:
- GNU Hello version `2.12.3`
- expected Guix nix-base32 source hash `183a6rxnhixiyykd7qis0y9g9cfqhpkk872a245y3zl28can0pqd`
- verified the downloaded tarball against the translated SHA256:
- `0d5f60154382fee10b114a1c34e785d8b1f492073ae2d3a6f7b147687b366aa0`
- successfully executed the standard native build lifecycle on `FreeBSD 15.0-STABLE` amd64:
- fetch
- hash verification
- extract
- configure
- build
- staged install
- runtime execution
- confirmed the staged binary runs and prints:
- `Hello, world!`
- captured build metadata including:
- compiler and make versions
- host triplet
- configure command
- staged output path
- runtime shared-library dependencies
- wrote the results to `docs/reports/phase1-native-gnu-hello.md`
Important findings:
- GNU Hello built successfully with FreeBSD base `make`, not just `gmake`
- that contrasts with the earlier local Guile build, which did require GNU `gmake`
- even this minimal GNU package links against FreeBSD-userland-provided libraries such as `libiconv` and `libintl`, which is useful data for later Guix package modeling
- this step is still a native shell-driven build exercise, not yet a real Guix package build
Current assessment:
- Phase 1.2 now has a concrete native autotools success case on FreeBSD
- the host can perform the basic fetch/verify/configure/build/install/run cycle needed for later `gnu-build-system` adaptation work
- Guix-specific build orchestration is still missing, but the environmental baseline is stronger now
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
Next recommended step:
1. extend Phase 1.2 with at least one additional representative GNU/autotools package build on FreeBSD, or
2. prototype a tiny Scheme-based `gnu-build-system`-like phase runner using the known-good local Guile path, starting from the GNU Hello flow
3. continue keeping `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: Guix builder-side GNU Hello phase runner validated
Completed work:
- added a Scheme-driven GNU Hello build prototype:
- `tests/native-build/gnu-hello-guix-phase-runner.scm`
- `tests/native-build/run-gnu-hello-guix-phase-runner.sh`
- required the previously validated fixed local Guile build for this harness because it depends on subprocess-heavy Scheme operations
- used Guix modules directly from `~/repos/guix`, including:
- `(guix base32)`
- `(guix build gnu-build-system)`
- `(guix build utils)`
- fetched and hash-verified GNU Hello `2.12.3` again against the Guix package hash:
- nix-base32: `183a6rxnhixiyykd7qis0y9g9cfqhpkk872a245y3zl28can0pqd`
- SHA256: `0d5f60154382fee10b114a1c34e785d8b1f492073ae2d3a6f7b147687b366aa0`
- successfully executed a subset of Guix builder-side `%standard-phases` on FreeBSD:
- `set-SOURCE-DATE-EPOCH`
- `unpack`
- `configure`
- `build`
- `check`
- `install`
- installed GNU Hello into a store-like output path under the temporary work directory rather than using a `/usr/local` `DESTDIR` staging layout
- executed the resulting binary successfully and confirmed output:
- `Hello, world!`
- captured metadata including:
- host triplet
- selected phase list
- runtime dependencies
- test-suite summary
- wrote the results to `docs/reports/phase1-guix-gnu-hello-phase-runner.md`
Important findings:
- this is the first validation step in the repo that successfully exercised actual Guix builder-side GNU build logic on FreeBSD instead of only a shell approximation
- the harness works when driven by the fixed local Guile build, confirming that the earlier Guile subprocess-fix validation is directly useful for FreeBSD Guix build orchestration
- GNU Hello's `make check` test suite also passed in this mode:
- total: `7`
- pass: `7`
- fail: `0`
- the resulting binary's runtime dependencies differ from the earlier `/usr/local`-prefixed native shell harness; in this store-like output layout it only showed:
- `libc.so.7`
- `libsys.so.7`
- that difference is a useful clue that Guix-style output layout/build invocation can materially affect FreeBSD runtime linkage behavior
Current assessment:
- Phase 1.2 now has both:
- a shell-driven native GNU Hello build harness, and
- a Scheme-driven prototype that uses real Guix builder-side GNU phases
- this is still short of a true Guix package/derivation build, but it significantly narrows the gap between host validation and real `gnu-build-system` execution on FreeBSD
- the known-good local Guile path is now validated as part of a practical Guix-adjacent build workflow, not just standalone subprocess diagnostics
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
Next recommended step:
1. run the Scheme-driven phase-runner pattern against at least one more small GNU/autotools package on FreeBSD, or
2. document the concrete gaps between this prototype and a real Guix package/derivation build, especially around store management and build isolation
3. continue keeping `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: second Scheme-driven GNU package build validated with GNU which
Completed work:
- added a second Scheme-driven GNU package harness:
- `tests/native-build/gnu-which-guix-phase-runner.scm`
- `tests/native-build/run-gnu-which-guix-phase-runner.sh`
- again used the previously validated fixed local Guile build because this harness depends on subprocess-heavy Guix/Scheme builder logic
- used the current Guix package definition in `~/repos/guix/gnu/packages/base.scm` as the source of truth for:
- GNU which version `2.21`
- expected Guix nix-base32 source hash `1bgafvy3ypbhhfznwjv1lxmd6mci3x1byilnnkc7gcr486wlb8pl`
- verified the downloaded tarball against the translated SHA256:
- `f4a245b94124b377d8b49646bf421f9155d36aa7614b6ebf83705d3ffc76eaad`
- successfully executed the same subset of Guix builder-side `%standard-phases` on FreeBSD as used for GNU Hello:
- `set-SOURCE-DATE-EPOCH`
- `unpack`
- `configure`
- `build`
- `check`
- `install`
- executed the resulting `which` binary successfully with a deterministic command:
- `PATH=/bin:/usr/bin ./which sh`
- confirmed output:
- `/bin/sh`
- captured metadata including:
- host triplet
- phase list
- runtime dependencies
- check-phase success status
- executed command output
- wrote the results to `docs/reports/phase1-guix-which-phase-runner.md`
Important findings:
- this confirms the Scheme-driven Guix builder-side phase-runner pattern is not limited to GNU Hello; a second small GNU/autotools package also succeeds on FreeBSD
- GNU which's `check` phase passed, but it did not leave behind an Automake-style `test-suite.log` or `testsuite.log`
- GNU which emitted a non-fatal `configure` warning about Guix's standard `--enable-fast-install` flag being unrecognized
- the source also emitted several clang warnings about deprecated non-prototype C declarations/definitions, but the build still completed successfully
- the resulting `which` binary again showed a minimal store-like runtime linkage profile:
- `libc.so.7`
- `libsys.so.7`
- unlike GNU Hello, the source tree did not present an obvious shipped `config.guess`, so the harness used `cc -dumpmachine` as a fallback for host-triplet metadata
Current assessment:
- Phase 1.2 now has two successful Scheme-driven Guix builder-side GNU package validations on FreeBSD:
- GNU Hello
- GNU which
- this increases confidence that a narrow but real subset of `gnu-build-system` builder-side execution already works on FreeBSD when paired with the fixed local Guile build
- the next uncertainty is now less about whether basic builder phases run at all, and more about where real Guix package/derivation/store integration and isolation will first require FreeBSD-specific adaptation
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
Next recommended step:
1. document the concrete remaining gap between these Scheme-driven phase-runner prototypes and a true Guix package/derivation/store-daemon build on FreeBSD, especially around store management, implicit inputs, and build isolation
2. or choose a somewhat more demanding GNU package with non-trivial declared inputs to identify the first builder-side FreeBSD adaptation points
3. continue keeping `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: documented the gap to a real Guix package build
Completed work:
- analyzed the concrete remaining gap between the current FreeBSD validation harnesses and a real Guix package/derivation/store-daemon build
- wrote the analysis to:
- `docs/reports/phase1-guix-build-gap-analysis.md`
- based the analysis on the current local Guix source tree, including the relevant host-side and daemon-side code paths in:
- `guix/packages.scm`
- `guix/build-system/gnu.scm`
- `guix/gexp.scm`
- `guix/store.scm`
- `guix/derivations.scm`
- `gnu/packages/commencement.scm`
- `doc/guix.texi`
- `nix/libstore/build.cc`
- compared those real Guix layers against the currently validated FreeBSD prototypes:
- shell-driven native GNU Hello harness
- Scheme-driven GNU Hello builder-phase runner
- Scheme-driven GNU which builder-phase runner
Main conclusions recorded:
- the current FreeBSD work has validated a narrow but real builder-side slice of Guix execution:
- Guile can run the needed Scheme code when using the fixed local build
- `(guix build gnu-build-system)` phases can build small GNU packages on FreeBSD
- however, the current prototypes still bypass several critical layers of a real Guix build:
- package -> bag lowering
- bag -> derivation lowering
- imported module closure/store materialization
- real daemon RPC and build submission
- canonical `/gnu/store` management and metadata
- build users, chroot/container or jail-style isolation
- substitute/graft/offload handling
- documented that the current phase runners rely on host tools already present on FreeBSD, whereas real `gnu-build-system` uses implicit inputs drawn from `%final-inputs`
- identified the most actionable next milestone as a derivation-generation investigation for a tiny package, to locate the first failure boundary among:
- host-side lowering
- store interaction
- derivation emission
- daemon availability
- daemon-side execution
Current assessment:
- Phase 1.2 now has both practical build validation and a clearer architectural map of what remains before a true Guix package build can work on FreeBSD
- the project has reduced uncertainty around builder-side GNU phase portability
- the next uncertainty is now specifically the host-side lowering/store/daemon boundary, not the builder-phase boundary
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
Next recommended step:
1. investigate whether a tiny package can be lowered far enough on FreeBSD to produce a real derivation, and capture the exact first failure point
2. if derivation generation proves immediately blocked, document whether the blocker is generated Guix modules/configuration, store connectivity, or daemon assumptions
3. continue keeping `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: derivation-generation investigation identified the first real checkout blockers
Completed work:
- added a reproducible checkout/bootstrap/configure investigation harness:
- `tests/guix/run-derivation-generation-investigation.sh`
- used the previously validated fixed local Guile build for the investigation:
- `/tmp/guile-freebsd-validate-install/bin/guile`
- followed the operator instruction for future store setup by parameterizing the checkout attempts to use:
- store directory: `/frx/store`
- local state directory: `/frx/var`
- sysconf directory: `/frx/etc`
- created a disposable shared clone of `~/repos/guix`
- successfully ran `./bootstrap` from that disposable checkout on FreeBSD
- attempted `configure` without `--with-courage`
- attempted `configure` again with `--with-courage`
- confirmed directly that the local fixed Guile build currently cannot load:
- `(gnutls)`
- wrote the results to:
- `docs/reports/phase1-guix-derivation-generation-investigation.md`
Important findings:
- a stock Guix checkout currently fails configuration on FreeBSD before any derivation/store/daemon work is reached, due to the explicit unsupported-platform gate:
- ``configure: error: `x86_64-freebsd15.0' is not a supported platform.``
- re-running `configure` with `--with-courage` gets past that gate, but then stops on a second blocker:
- `configure: error: The Guile bindings of GnuTLS are missing; please install them.`
- a direct module-load test with the same local Guile confirms the problem more concretely:
- `(use-modules (gnutls))` fails with `no code for module (gnutls)`
- this means the current effort is still blocked before reaching:
- usable `pre-inst-env` generation for Guix commands
- package -> bag lowering in a live checkout
- bag -> derivation lowering
- daemon connectivity
- actual `/frx/store` population
Current assessment:
- the first practical boundary between the earlier FreeBSD builder-phase prototypes and a real Guix checkout has now been located more precisely
- the project is no longer blocked by vague uncertainty at this stage; it is blocked by two concrete checkout-preparation issues:
1. unsupported-platform configure gating
2. missing Guile `(gnutls)` bindings
- importantly, the derivation-generation investigation has not yet reached the store/daemon boundary, so the next step must first clear the `(gnutls)` dependency issue
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
Next recommended step:
1. obtain working Guile `(gnutls)` bindings compatible with the fixed local Guile build and re-run the derivation-generation investigation
2. once configuration succeeds, continue until the next failure boundary is identified among:
- `pre-inst-env` usability
- derivation emission
- daemon connectivity
- daemon-side `/frx/store` assumptions
3. continue keeping `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: local Guile-GnuTLS built on FreeBSD; next blocker is Guile-Git
Completed work:
- installed the host-side C GnuTLS stack needed for Guile-GnuTLS builds:
- `gnutls`
- `libtasn1`
- `nettle`
- `p11-kit`
- added a reproducible local Guile-GnuTLS build harness:
- `tests/guix/build-local-guile-gnutls.sh`
- updated the derivation-generation investigation harness so it can consume extra Guile module prefixes through:
- `GUILE_EXTRA_PREFIX`
- used the current Guix package definition in `~/repos/guix/gnu/packages/tls.scm` as the source of truth for:
- `guile-gnutls` version `5.0.1`
- expected Guix nix-base32 source hash `0kqngyx4520gjk49l6whjd2ss994kaj9rm78lli6p3q6xry0945i`
- verified the downloaded Guile-GnuTLS tarball against the translated SHA256:
- `b190047cee068f6b22a5e8d49ca49a2425ad4593901b9ac8940f8842ba7f164f`
- built and installed a local Guile-GnuTLS validation copy against the previously validated fixed local Guile build under:
- `/tmp/guile-gnutls-freebsd-validate-install`
- validated successfully that the fixed local Guile can now load:
- `(gnutls)`
- re-ran the checkout derivation-generation investigation with:
- `GUILE_EXTRA_PREFIX=/tmp/guile-gnutls-freebsd-validate-install`
- store directory still set to `/frx/store`
- wrote the results to:
- `docs/reports/phase1-guile-gnutls-freebsd.md`
Important findings:
- Guile-GnuTLS does not build with FreeBSD base `make`; it requires GNU `gmake`
- a FreeBSD-specific source compatibility issue surfaced in `guile/src/core.c`:
- it includes `<alloca.h>` unconditionally
- on this host, `alloca` is available through `<stdlib.h>` instead
- for local validation, the harness applies a small disposable-tree patch that uses `<stdlib.h>` on `__FreeBSD__`
- after that fix, the local Guile-GnuTLS build succeeded and `(use-modules (gnutls))` worked with the fixed local Guile build
- re-running the Guix checkout investigation confirms the earlier `(gnutls)` blocker is genuinely cleared
- the next configure-time blocker is now:
- `configure: error: Guile-Git is missing; please install it.`
Current assessment:
- the first checkout-preparation blocker after unsupported-platform gating has advanced from missing `(gnutls)` to missing `Guile-Git`
- this is meaningful progress because the project is now moving farther into the dependency chain required for a real Guix checkout on FreeBSD
- the requested experimental store path remains `/frx/store`, but the effort still has not yet reached actual store population or daemon interaction
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
Next recommended step:
1. obtain `Guile-Git` compatible with the fixed local Guile build and re-run the derivation-generation investigation again
2. once checkout configuration succeeds, continue until the next failure boundary is identified among:
- `pre-inst-env` usability
- derivation emission
- daemon connectivity
- daemon-side `/frx/store` assumptions
3. continue keeping `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: local Guile-Git stack built on FreeBSD; next blocker is Guile-JSON
Completed work:
- added a reproducible local Guile-Git dependency-stack build harness:
- `tests/guix/build-local-guile-git.sh`
- updated the derivation-generation investigation harness to probe and record both:
- local `(gnutls)` availability
- local `(git)` / `graph-descendant?` availability
- used the current Guix package definitions in `~/repos/guix/gnu/packages/guile.scm` as the source of truth for:
- `guile-bytestructures` version `1.0.10`
- `guile-git` version `0.10.0`
- built `guile-bytestructures` from the matching upstream tag and recorded resolved commit:
- `27cadba6b69a01b38b33bb39b9766d713eb90c1b`
- built `guile-git` from the matching upstream tag and recorded resolved commit:
- `05d4a48c811f29c8db80ee6697fe658950fb503e`
- installed both into the same local dependency prefix already used for the earlier Guile-GnuTLS validation:
- `/tmp/guile-gnutls-freebsd-validate-install`
- validated successfully that the fixed local Guile can now load:
- `(bytestructures guile)`
- `(git)`
- validated specifically that the Guile-Git export required by Guix `configure.ac` is present:
- `graph-descendant?`
- confirmed the host `libgit2` dependency used for the build is:
- `1.9.2`
- re-ran the checkout derivation-generation investigation with:
- `GUILE_EXTRA_PREFIX=/tmp/guile-gnutls-freebsd-validate-install`
- store directory still set to `/frx/store`
- wrote the results to:
- `docs/reports/phase1-guile-git-freebsd.md`
Important findings:
- both `guile-bytestructures` and `guile-git` were built from Git source layouts, so autotools regeneration was required:
- `guile-bytestructures`: `autoreconf -vfi`
- `guile-git`: `autoreconf -vfi` via harness fallback
- unlike the earlier Guile-GnuTLS step, no additional FreeBSD-specific source patch was needed for either package in this validation pass
- the Guix checkout now gets past both previously cleared dependency gates:
- `(gnutls)`
- `Guile-Git`
- after clearing those, the next configure-time blocker is now:
- `configure: error: Guile-JSON is missing; please install it.`
Current assessment:
- the checkout-preparation path on FreeBSD has progressed one dependency layer deeper
- the local validation prefix under `/tmp/guile-gnutls-freebsd-validate-install` now contains at least:
- Guile-GnuTLS
- Guile bytestructures
- Guile-Git
- despite that progress, the work still has not yet reached derivation emission, daemon connectivity, or actual `/frx/store` population
- the next concrete blocker is now `Guile-JSON`, as reported directly by the real Guix checkout configure step
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
Next recommended step:
1. obtain `Guile-JSON` compatible with the fixed local Guile build and install it into the same local dependency prefix
2. re-run the derivation-generation investigation again to identify the next configure-time or checkout-time blocker after `Guile-JSON`
3. continue keeping `/frx/store` as the intended experimental store root and keep `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: local Guile-JSON built on FreeBSD; next blocker is Guile-SQLite3
Completed work:
- added a reproducible local Guile-JSON build harness:
- `tests/guix/build-local-guile-json.sh`
- updated the derivation-generation investigation harness to probe and record local recent-enough `(json)` availability in addition to the earlier `(gnutls)` and `(git)` checks
- used the current Guix package definition in `~/repos/guix/gnu/packages/guile.scm` as the source of truth for:
- `guile-json` version `4.7.3`
- expected Guix nix-base32 source hash `127k2xc07w1gnyqs40z4865l8p3ra5xgpcn569dz04lxsa709fiq`
- verified the downloaded Guile-JSON tarball against the translated SHA256:
- `38ba048ed29d12f05b32c5b2fb7a51795c448b41e403a2b1b72ff0035817f388`
- built and installed a local Guile-JSON validation copy into the same local dependency prefix already used for checkout prerequisites:
- `/tmp/guile-gnutls-freebsd-validate-install`
- validated successfully that the fixed local Guile now satisfies the Guix configure-time JSON requirement by:
- loading `(json)`
- using `define-json-mapping`
- decoding a small JSON object successfully
- re-ran the checkout derivation-generation investigation with:
- `GUILE_EXTRA_PREFIX=/tmp/guile-gnutls-freebsd-validate-install`
- store directory still set to `/frx/store`
- wrote the results to:
- `docs/reports/phase1-guile-json-freebsd.md`
Important findings:
- unlike the earlier Guile-Git step, Guile-JSON built cleanly from the release tarball and did not require autotools regeneration in this validation pass
- unlike the earlier Guile-GnuTLS step, no FreeBSD-specific source patch was needed here
- the shared local validation prefix now contains at least:
- Guile-GnuTLS
- Guile bytestructures
- Guile-Git
- Guile-JSON
- the Guix checkout now gets past the previously cleared dependency gates for:
- `(gnutls)`
- `Guile-Git`
- `Guile-JSON`
- after clearing those, the next configure-time blocker is now:
- `configure: error: A recent Guile-SQLite3 could not be found; please install it.`
Current assessment:
- the checkout-preparation path on FreeBSD has progressed another dependency layer deeper
- the project still has not yet reached derivation emission, daemon connectivity, or actual `/frx/store` population
- the next concrete blocker is now recent `Guile-SQLite3`, as reported directly by the real Guix checkout configure step
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
Next recommended step:
1. obtain recent `Guile-SQLite3` compatible with the fixed local Guile build and install it into the same local dependency prefix
2. re-run the derivation-generation investigation again to identify the next configure-time or checkout-time blocker after `Guile-SQLite3`
3. continue keeping `/frx/store` as the intended experimental store root and keep `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.2 follow-up: remaining checkout Guile prerequisites built; next blocker is `./pre-inst-env guix --version`
Completed work:
- installed the remaining missing host C library dependencies required for the next Guile extension stack:
- `libgcrypt`
- `libgpg-error`
- `lzlib`
- added a reproducible build harness for the remaining mandatory Guix checkout Guile modules:
- `tests/guix/build-local-guile-configure-deps.sh`
- extended the derivation-generation investigation harness to:
- probe local recent-enough availability for:
- `(sqlite3)`
- `(gcrypt hash)`
- `(zlib)`
- `(lzlib)`
- `(semver)`
- run checkout `configure` with:
- `MAKE=gmake`
- continue past successful configuration into:
- `gmake scripts/guix`
- `./pre-inst-env guix --version`
- used the current Guix package definitions as source of truth for the following additional module stack:
- `guile-sqlite3` `0.1.3`
- `guile-gcrypt` `0.5.0`
- `guile-zlib` `0.2.2`
- `guile-lzlib` `0.3.0`
- `guile-semver` `0.2.0`
- built and installed those modules into the same shared local dependency prefix already used for prior checkout prerequisites:
- `/tmp/guile-gnutls-freebsd-validate-install`
- validated successfully that the fixed local Guile can now satisfy all of the remaining configure-time Guix module checks encountered so far:
- `(sqlite3)` with `sqlite-bind-arguments`
- `(gcrypt hash)` with `hash-algorithm` lookup
- `(zlib)` with `make-zlib-input-port`
- `(lzlib)`
- `(semver)`
- re-ran the checkout derivation-generation investigation with:
- `GUILE_EXTRA_PREFIX=/tmp/guile-gnutls-freebsd-validate-install`
- store directory still set to `/frx/store`
- wrote the results to:
- `docs/reports/phase1-guix-checkout-configure-stack-freebsd.md`
Important findings:
- `guile-gcrypt` required an explicit configure workaround on this host:
- `--with-libgcrypt-prefix=/usr/local`
- without it, the package's `libgcrypt-config --libs` parsing produced an unusable shared-library name on FreeBSD
- the currently served upstream `guile-lzlib` `0.3.0` tarball no longer matches the Guix-recorded hash:
- expected from Guix: `a7f99c8d2a143e05ea22db2dc8b9ce6c27cae942162b45ee3015ed9027af0ff2`
- observed from current source URL: `6a2847a303a141bb95b1b5d1a4b975b4dbff9cc590eba377cc8072682e7637ec`
- for local validation, the harness fell back to the matching upstream Git tag and recorded commit:
- `474cee42116295bc0bd2acf12d4d6a766043090e`
- once the remaining Guile modules were present, checkout `configure --with-courage` stopped failing on missing modules
- however, the checkout still needed:
- `MAKE=gmake`
to complete configuration successfully on FreeBSD
- after that, `gmake scripts/guix` succeeded as well
- the next concrete blocker has moved from configuration-time prerequisites to runtime behavior of the uninstalled Guix command path:
- `./pre-inst-env guix --version` prints the version banner, then exits with:
- `Wrong type to apply: #<syntax-transformer leave-on-EPIPE>`
Current assessment:
- the checkout-preparation path on FreeBSD has now progressed beyond the missing mandatory Guile module stack that previously blocked configuration
- the current local validation prefix now contains the required configure-time modules encountered so far for a real Guix checkout
- the first blocker after successful checkout configuration and `scripts/guix` generation is now a runtime Scheme failure in the uninstalled `guix` command path itself
- the work is therefore now meaningfully past “cannot configure” and into “configures, builds `scripts/guix`, but fails at `./pre-inst-env guix --version`
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
Next recommended step:
1. investigate the `leave-on-EPIPE` runtime failure now blocking `./pre-inst-env guix --version`
2. complete the remaining Phase 1.3 FreeBSD system-call mapping/documentation deliverable so Phase 1 foundations can be closed out cleanly
3. continue keeping `/frx/store` as the intended experimental store root and keep `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 1.3 completed: FreeBSD syscall/interface mapping documented and exercised
Completed work:
- added a runnable C syscall/interface mapping harness:
- `tests/system/freebsd-syscall-mapping.c`
- added a shell runner for the mapping harness:
- `tests/system/run-freebsd-syscall-mapping.sh`
- inspected current Guix/Linux-oriented source paths relevant to daemon/build isolation and host behavior, especially:
- `~/repos/guix/nix/libstore/build.cc`
- `~/repos/guix/configure.ac`
- inspected the relevant FreeBSD interfaces/documentation available on the host, including:
- `jail(2)`
- `chroot(2)`
- `closefrom(2)` / `close_range(2)`
- `mount(2)` / `nmount(2)`
- `mount_nullfs(8)`
- `cap_enter(2)`
- `cap_rights_limit(2)`
- `lutimes(2)`
- `lchown(2)`
- `posix_fallocate(2)`
- `pdfork(2)`
- ran the new syscall mapping harness successfully and captured metadata under:
- `/tmp/freebsd-syscall-mapping-metadata.txt`
- wrote the Phase 1.3 report to:
- `docs/reports/phase1-freebsd-syscall-mapping.md`
Important findings:
- the current FreeBSD host provides and the harness successfully exercised:
- `fork` / `waitpid`
- `posix_spawn_file_actions_addclosefrom_np`
- `close_range`
- `lutimes`
- `statvfs`
- `chroot` (root test)
- `jail(2)` (root test)
- `lchown` (root test)
- the same harness confirmed the absence of the key Linux namespace-oriented interfaces that current Guix daemon code depends on:
- `clone`
- `unshare`
- `setns`
- `pivot_root`
- `sys/prctl.h`
- FreeBSD Capsicum headers are present, which is useful context for later security design, but Capsicum is not a direct replacement for Linux namespaces or Linux capabilities
- `posix_fallocate` is present but returned `EOPNOTSUPP` on the tested filesystems, so a successful configure/link probe does not guarantee useful runtime semantics
- the mapping strongly confirms that the correct Phase 2 direction is not syscall emulation but a jail-first redesign using:
- jails
- `chroot`
- `nullfs`
- traditional build-user privilege separation
Current assessment:
- the Phase 1.3 deliverable is now satisfied with both:
- a technical mapping document, and
- a runnable validation harness
- the main architectural conclusion is now concrete rather than speculative:
- Linux namespace code paths in current Guix daemon/build isolation cannot be ported directly to FreeBSD
- the FreeBSD implementation must instead be designed around jails and explicit mount/layout control
## 2026-04-01 — Phase 1 completed on the current FreeBSD amd64 porting track
Phase 1 is now considered complete for the active amd64 FreeBSD host path.
Why this milestone is satisfied:
- **Phase 1.1** success criteria were met on the current host:
- Guile executes Guix bootstrap-related code
- deterministic/module/FFI/socket/process validation succeeded
- the FreeBSD subprocess crash was root-caused and a working fixed local Guile path was validated
- **Phase 1.2** success criteria were exceeded:
- native GNU Hello build success was demonstrated
- multiple Guix builder-side GNU phase validations succeeded
- a real Guix checkout now configures on FreeBSD with local dependency supplementation, builds `scripts/guix`, and reaches a concrete runtime blocker at:
- `./pre-inst-env guix --version`
- **Phase 1.3** is now completed with the syscall/interface mapping document and runnable harness
Important scope note:
- the original Phase 1.1 narrative mentioned `i386` as additional target coverage, but the explicit success criteria used to gate progression have been satisfied on the active amd64 FreeBSD host
- full i386 Guile validation remains useful future coverage work, but it is no longer the blocker for moving into Phase 2 design/prototyping on this machine
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
Next recommended step:
1. begin Phase 2.1 by turning the new syscall mapping into a concrete FreeBSD jail-based build-isolation design/prototype
2. carry forward the current concrete runtime blocker from Phase 1.2:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
3. continue keeping `/frx/store` as the intended experimental store root and keep `~/repos/bdwgc` in reserve if later FreeBSD-specific GC/thread issues appear
## 2026-04-01 — Phase 2.1 completed: jail-first build isolation design validated on FreeBSD
Completed work:
- added a runnable jail-based build isolation prototype:
- `tests/daemon/run-freebsd-jail-build-prototype.sh`
- wrote the Phase 2.1 design/prototype report:
- `docs/reports/phase2-freebsd-jail-build-isolation.md`
- translated the earlier Phase 1 syscall mapping into a concrete FreeBSD Guix-daemon isolation design centered on:
- thin jails
- one jail per build
- explicit `nullfs` mount plans
- networking disabled by default
- separate build-user credentials inside the jail envelope
- ran the jail prototype successfully and captured metadata under:
- `/tmp/jail-build-metadata.txt`
Important findings:
- a thin-jail approach is the right match for Guix's declared-input model; thick jails would overexpose ambient host state and add unnecessary duplication
- a per-build jail root assembled from explicit read-only `nullfs` mounts is a practical replacement for the Linux bind-mount + mount-namespace model in current Guix daemon code
- a basic build operation can already be executed successfully inside a FreeBSD jail with a restricted filesystem view consisting only of:
- selected read-only host toolchain paths
- a read-only declared input directory
- a writable declared output directory
- a writable `/tmp`
- a host sentinel file left outside the jail root is not visible inside the build environment, confirming the prototype is exercising real visibility restriction rather than a mere chroot-like shell wrapper
- the prototype jail ran with:
- `ip4=disable`
- `ip6=disable`
which matches the intended default for hermetic builds
Current assessment:
- Phase 2.1 is now satisfied on the current FreeBSD prototype track
- the main design decision is now concrete rather than speculative:
- the Guix FreeBSD daemon path should be jail-first, not Linux-namespace emulation
- the next step is to add a build-user privilege-dropping prototype inside or alongside this jail model so the design covers both containment and user-level isolation
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
Next recommended step:
1. implement the Phase 2.2 privilege-dropping/build-user prototype for FreeBSD, ideally combined with the new jail execution model
2. then establish a `/frx/store`-based store-management prototype covering permissions, package readability, and garbage-collection behavior
3. continue carrying the separate Guix checkout runtime blocker:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
## 2026-04-01 — Phase 2.2 completed: privilege dropping and concurrent build-user isolation validated
Completed work:
- added a C helper implementing the core daemon-side privilege drop mechanics:
- `tests/daemon/freebsd-build-user-helper.c`
- added a harness that combines that helper with the new jail model and runs two jobs concurrently:
- `tests/daemon/run-freebsd-privilege-drop-prototype.sh`
- wrote the Phase 2.2 report:
- `docs/reports/phase2-freebsd-privilege-drop.md`
- ran the concurrent build-user prototype successfully and captured metadata under:
- `/tmp/freebsd-privdrop-metadata.txt`
Important findings:
- a root-launched FreeBSD helper can successfully perform the expected daemon-side transition:
- `setgroups`
- `setgid`
- `setuid`
into a dedicated build identity
- once dropped, the helper cannot regain root with `setuid(0)`:
- `Operation not permitted`
- each build job can create files in its own writable directory and those files end up owned by the dropped build UID/GID rather than by root
- two concurrent jobs using distinct numeric build identities succeeded with:
- job 1 UID/GID `35001:35001`
- job 2 UID/GID `35002:35002`
- host-side result files were observed with the matching ownership and restrictive permissions:
- `0600`
- the two jobs were deliberately held for two seconds each and the measured wall-clock elapsed time was also about two seconds, demonstrating actual concurrent execution rather than serialized execution
- two complementary denial modes were validated at the same time:
- peer build files mounted but blocked by permissions: `Permission denied`
- host path not mounted into the jail at all: `No such file or directory`
- the dropped build user also could not:
- create files in a protected root-owned directory
- `chown` its own output back to root
Current assessment:
- Phase 2.2 is now satisfied on the current FreeBSD prototype track
- the combined jail + build-user model now has practical validation for the most important security properties required by a future FreeBSD Guix daemon:
- root-controlled setup
- permanent drop to build credentials
- per-build writable areas
- cross-build isolation
- concurrent execution under distinct identities
- the remaining Phase 2 work is now centered on the store itself: permissions, readability, content-addressed layout, and garbage-collection behavior under `/frx/store`
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
Next recommended step:
1. complete Phase 2.3 by establishing a `/frx/store`-based store prototype with:
- correct root/daemon write restrictions
- unprivileged read access
- content-addressed path naming
- garbage-collection behavior
2. if possible, use outputs or dependency relationships realistic enough to model how a future FreeBSD Guix daemon would retain referenced store items
3. continue carrying the separate Guix checkout runtime blocker:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
## 2026-04-01 — Phase 2.3 completed: `/frx/store` prototype validated on FreeBSD
Completed work:
- added a runnable `/frx/store` prototype harness:
- `tests/store/run-freebsd-store-prototype.sh`
- wrote the Phase 2.3 report:
- `docs/reports/phase2-freebsd-store-prototype.md`
- created and exercised the operator-requested `/frx` layout on-host:
- `/frx/store`
- `/frx/var`
- `/frx/etc`
- `/frx/var/fruix/gcroots`
- created a store group for the prototype path:
- `fruixbuild`
- ran the store prototype successfully and captured metadata under:
- `/tmp/freebsd-store-prototype-metadata.txt`
Important findings:
- the current host now has a working `/frx/store` prototype owned as:
- `root:fruixbuild`
with mode:
- `drwxrwxr-t`
- the prototype successfully created content-addressed demo store items under `/frx/store` using hash-based names
- the demo item set included:
- rooted greeting data
- a rooted app referencing that data through an absolute store path
- an unrooted orphan item intended for collection
- an unprivileged user (`nobody`) could:
- read store data
- execute the demo app from the store
- the same unprivileged user could not:
- create files directly in `/frx/store`
and the observed failure was:
- `Permission denied`
- the prototype GC logic followed rooted references successfully:
- with a GC root present, the app and its referenced data survived while the orphan item was collected
- after removing the GC root, the remaining demo items were collected as well
- the demo store returned to an empty state after the second GC pass, so the host is left with the `/frx` skeleton but without lingering prototype payloads
Current assessment:
- Phase 2.3 is now satisfied on the current FreeBSD prototype track
- the core store assumptions needed for a FreeBSD Guix-daemon design have practical validation now:
- `/frx/store` path viability
- root-controlled mutation
- unprivileged read access
- immutable absolute store references
- root-managed GC roots and mark/sweep retention behavior
- remaining gaps are now above this architectural layer rather than below it:
- real derivation registration
- SQLite-backed store metadata
- daemon RPC integration
- actual package lowering/build submission using these mechanisms
## 2026-04-01 — Phase 2 completed on the current FreeBSD prototype track
Phase 2 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 2.1** success criteria were met:
- a detailed jail-first build-isolation design was produced
- a runnable prototype successfully executed a build command in a restricted FreeBSD jail
- **Phase 2.2** success criteria were met:
- a concrete C privilege-dropping implementation was added
- build-user credential drop, inability to regain root, and concurrent cross-build isolation were demonstrated
- **Phase 2.3** success criteria were met on the prototype track:
- a working `/frx/store` equivalent was established
- content-addressed demo store items were created and consumed
- unprivileged read vs. privileged write behavior was validated
- garbage-collection behavior over rooted references was demonstrated
Important scope note:
- this completes the **core daemon architecture adaptation** milestone, not a full Guix-daemon port
- the separate real-checkout blocker from Phase 1 remains relevant for later integration work:
- `./pre-inst-env guix --version` still fails with `Wrong type to apply: #<syntax-transformer leave-on-EPIPE>`
- however, that runtime issue no longer blocks the specific Phase 2 architectural deliverables because the jail, privilege, and store assumptions have now been validated independently on FreeBSD
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
- `d65b2af``Prototype FreeBSD build user isolation`
Next recommended step:
1. begin Phase 3.1 by adapting Guix build-system expectations to the now-validated jail/privilege/store model on FreeBSD
2. carry forward the concrete real-checkout runtime blocker for later integration work:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
3. continue using `/frx/store` rather than `/gnu/store` for FreeBSD store experiments
## 2026-04-01 — Phase 3.1 completed: reusable FreeBSD GNU build-system adaptation validated across five packages
Completed work:
- added a reusable Scheme runner for FreeBSD-adapted GNU package builds:
- `tests/build-system/gnu-package-freebsd-phase-runner.scm`
- added a shell wrapper for the generic runner:
- `tests/build-system/run-gnu-package-freebsd-phase-runner.sh`
- added a five-package validation matrix:
- `tests/build-system/run-freebsd-gnu-package-matrix.sh`
- wrote the Phase 3.1 report:
- `docs/reports/phase3-freebsd-gnu-build-system.md`
- ran the matrix successfully and captured summary metadata under:
- `/tmp/freebsd-gnu-package-matrix-summary.txt`
Important findings:
- the build adaptation is now centralized rather than package-specific and is applied through a dedicated pre-configure FreeBSD environment phase
- the adaptation consistently uses:
- GNU `gmake` via a `make` path shim
- FreeBSD Clang via `cc`/`gcc` and `c++`/`g++` tool shims
- `CONFIG_SHELL=/bin/sh`
- `/usr/local` include/library/pkg-config search paths
- five representative GNU packages from current Guix package definitions now build successfully through the adapted runner on the current FreeBSD amd64 host:
- `hello` `2.12.3`
- `which` `2.21`
- `time` `1.9`
- `patch` `2.8`
- `nano` `8.7.1`
- the resulting binaries executed correctly with deterministic checks appropriate to each package:
- `hello` -> `Hello, world!`
- `which` -> `/bin/sh`
- `time` -> `time (GNU Time) 1.9`
- `patch` -> `GNU patch 2.8`
- `nano` -> `GNU nano, version 8.7.1`
- the matrix also validated a package with meaningful runtime dependencies:
- `nano` linked against FreeBSD/base and `/usr/local` libraries including `libintl`, `libmagic`, `libncursesw`, `libtinfow`, and `libz`
- one package-specific FreeBSD test boundary was recorded explicitly instead of being hidden:
- `time` required `RUN_TESTS=0` because the upstream `time-max-rss` test was not reliable on this host
Current assessment:
- Phase 3.1 is now satisfied on the current prototype track
- the main question has shifted from “can adapted GNU builder phases run on FreeBSD?” to “how should FreeBSD system components themselves be described and installed as profile-usable packages?”
- the next step is therefore Phase 3.2: define a minimal FreeBSD package set with explicit dependencies and validate profile-style installation/usability
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
- `d65b2af``Prototype FreeBSD build user isolation`
- `e404e2e``Prototype FreeBSD store management`
Next recommended step:
1. complete Phase 3.2 by defining a minimal FreeBSD system package set with explicit dependency relationships and profile-style installation validation
2. carry forward the concrete real-checkout runtime blocker for later integration work:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
3. continue using `/frx/store` rather than `/gnu/store` for future FreeBSD store experiments when the prototype work needs a persistent store root
## 2026-04-01 — Phase 3.2 completed: FreeBSD system package-definition prototype and profile validation added
Completed work:
- added a Guix-style FreeBSD system package-definition prototype module:
- `modules/fruix/packages/freebsd.scm`
- added a Scheme harness to materialize those package definitions into store-like outputs and a merged profile:
- `tests/packages/freebsd-package-profile-prototype.scm`
- added a shell wrapper for that harness:
- `tests/packages/run-freebsd-package-profile-prototype.sh`
- installed the missing host shell dependency needed to satisfy the requested package set:
- `bash`
- wrote the Phase 3.2 report:
- `docs/reports/phase3-freebsd-package-definitions.md`
- ran the profile prototype successfully and captured metadata under:
- `/tmp/freebsd-package-profile-prototype-metadata.txt`
Important findings:
- the prototype now defines a minimal FreeBSD core package set covering the categories requested by Phase 3.2:
- kernel
- kernel headers
- libc
- userland utilities
- development tools (`clang`, `make`, autotools)
- minimum system libraries (`openssl`, `zlib`)
- shells (`sh`, `bash`)
- the current package-definition layer uses an explicit Guix-like record shape with fields for:
- name
- version
- build system
- inputs
- synopsis/description/home-page/license
- install plan
- explicit dependency relationships are now encoded and resolved recursively during materialization, including examples such as:
- `freebsd-libc` -> `freebsd-kernel-headers`
- `freebsd-userland` -> `freebsd-libc`, `freebsd-sh`
- `freebsd-clang-toolchain` -> `freebsd-libc`, `freebsd-kernel-headers`, `freebsd-sh`
- `freebsd-autotools` -> `freebsd-gmake`, `freebsd-bash`, `freebsd-libc`
- the harness successfully materialized:
- `11` core package outputs
into a store-like directory tree under the work directory
- it then merged those outputs into a development profile and validated that the profile contains working:
- `bash`
- `make`
- `autoconf`
- `cc`
- kernel image path
- kernel-header path
- core shared-library paths
- the generated profile compiled and ran a C test program successfully, with observed output:
- `hello-from-freebsd-profile`
- for executables installed under `bin/`, the prototype uses wrappers that `exec` the host tool by absolute path; this preserved correct behavior for prefix-sensitive tools such as `autoconf`
Current assessment:
- Phase 3.2 is now satisfied on the current prototype track
- Phase 3 as a whole is now completed on the current FreeBSD amd64 path because both:
- adapted GNU build-system execution, and
- minimal FreeBSD system package-definition/profile validation
have been demonstrated successfully
- the next remaining project milestone is now Phase 4, centered on Shepherd rather than package-building foundations
## 2026-04-01 — Phase 3 completed on the current FreeBSD prototype track
Phase 3 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 3.1** success criteria were met on the prototype track:
- a reusable FreeBSD adaptation layer for GNU builder phases was added
- five representative GNU packages built successfully through that adapted runner
- the resulting binaries executed correctly on the host
- **Phase 3.2** success criteria were met on the prototype track:
- a minimal FreeBSD system package-definition layer was added
- explicit dependency relationships were modeled and resolved
- the package outputs installed into a merged profile successfully
- the generated profile was validated by compiling and running a test program with the staged toolchain
Important scope note:
- this completes the **build-system adaptation milestone** in prototype form, not the full Guix package-lowering/daemon integration path
- the earlier concrete upstream/runtime blocker still exists for later integration work:
- `./pre-inst-env guix --version` fails with `Wrong type to apply: #<syntax-transformer leave-on-EPIPE>`
- however, that blocker no longer prevents Phase 4 work because the core build-system and package-definition assumptions have now been validated independently
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
- `d65b2af``Prototype FreeBSD build user isolation`
- `e404e2e``Prototype FreeBSD store management`
- `eb0d77c``Adapt GNU build phases for FreeBSD`
Next recommended step:
1. begin Phase 4.1 by validating whether Shepherd itself now builds and runs as a regular service on FreeBSD with the fixed local Guile path
2. carry forward the separate real-checkout runtime blocker for later integration work:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
3. continue using `/frx/store` rather than `/gnu/store` for future FreeBSD integration experiments when a persistent store root is required
## 2026-04-01 — Phase 4.1 completed: Shepherd built and validated as a regular FreeBSD service manager
Completed work:
- added a reproducible local Guile Fibers build harness:
- `tests/shepherd/build-local-guile-fibers.sh`
- added a reproducible local Shepherd build harness:
- `tests/shepherd/build-local-shepherd.sh`
- added a runnable multi-service Shepherd validation harness for FreeBSD:
- `tests/shepherd/run-freebsd-shepherd-service-prototype.sh`
- wrote the Phase 4.1 report:
- `docs/reports/phase4-freebsd-shepherd-service.md`
- ran the service-management prototype successfully and captured metadata under:
- `/tmp/freebsd-shepherd-service-metadata.txt`
Important findings:
- the current FreeBSD path now has a working local Shepherd build based on:
- local fixed Guile
- locally installed Guile Fibers `1.4.2`
- Shepherd `1.0.9`
- Shepherd build/install required one concrete FreeBSD-specific toolchain adaptation:
- `SED=/usr/local/bin/gsed`
because the install phase edits wrapper scripts using GNU `sed -i` syntax that base FreeBSD `sed` does not accept
- at runtime, Shepherd reports:
- `System lacks support for 'signalfd'; using fallback mechanism.`
but the fallback path works correctly for supervision on this host
- the prototype successfully validated all requested regular-service capabilities:
- start/stop via `herd`
- dependency handling
- status monitoring
- crash/respawn behavior
- privilege-aware execution
- the concrete service set used for validation included:
- an unprivileged heartbeat logger
- a loopback HTTP service
- a dependent file-monitor service
- a crash-once respawn test service
- observed metadata confirmed:
- `logger_running=yes`
- `web_running=yes`
- `monitor_running=yes`
- `crashy_running=yes`
- `logger_uid=65534` (`nobody`)
- `http_response=shepherd-freebsd-ok`
- `monitor_detected=detected`
- `crashy_counter=2`
Current assessment:
- Phase 4.1 is now satisfied on the current FreeBSD prototype track
- Shepherd is no longer just a theoretical later step; it now builds and supervises multiple services correctly on the host when paired with the fixed local Guile stack
- the next question is no longer “can Shepherd run on FreeBSD at all?” but “what is the best FreeBSD init-integration strategy for it on this prototype path?”
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
- `d65b2af``Prototype FreeBSD build user isolation`
- `e404e2e``Prototype FreeBSD store management`
- `eb0d77c``Adapt GNU build phases for FreeBSD`
- `d47dc9b``Prototype FreeBSD package definitions`
Next recommended step:
1. complete Phase 4.2 by prototyping how Shepherd should be launched and stopped through FreeBSD init conventions while validating boot/shutdown dependency ordering for essential services
2. after that, bridge Shepherd to key FreeBSD service concepts such as rc.d management, loopback/network configuration, filesystem setup, and temporary user/group administration
3. continue carrying the separate real-checkout runtime blocker for later integration work:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
## 2026-04-01 — Phase 4.2 completed: FreeBSD rc.d init-integration prototype validated for Shepherd
Completed work:
- added a runnable FreeBSD init-integration prototype harness:
- `tests/shepherd/run-freebsd-shepherd-init-prototype.sh`
- wrote the Phase 4.2 report:
- `docs/reports/phase4-freebsd-shepherd-init-integration.md`
- ran the init-integration prototype successfully and captured metadata under:
- `/tmp/freebsd-shepherd-init-metadata.txt`
Important findings:
- a real temporary FreeBSD `rc.d` script can successfully launch the locally built Shepherd daemon through the standard:
- `service <name> onestart`
path
- the same wrapper can stop it cleanly through:
- `service <name> onestop`
using `herd ... stop root` under the hood
- the prototype automatically started a minimal essential-service graph at daemon launch consisting of:
- `filesystems`
- `system-log`
- `networking`
- `login`
- observed startup order matched the declared dependency chain exactly:
- `start:filesystems`
- `start:system-log`
- `start:networking`
- `start:login`
- observed shutdown order matched the expected reverse dependency order exactly:
- `stop:login`
- `stop:networking`
- `stop:system-log`
- `stop:filesystems`
- the rc.d wrapper reported the Shepherd instance as running while active:
- `rc_status=running`
- the prototype again observed the expected FreeBSD runtime note:
- `System lacks support for 'signalfd'; using fallback mechanism.`
and confirmed that it does not prevent correct boot/shutdown ordering behavior
Current assessment:
- Phase 4.2 is now satisfied on the current prototype track as an init-integration prototype
- the key result is that Shepherd can already be launched and stopped through native FreeBSD service-management conventions while preserving dependency-based startup and shutdown semantics
- the remaining Phase 4 work is now specifically about bridging Shepherd services to concrete FreeBSD host-management concepts rather than basic daemon launch or service ordering
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
- `d65b2af``Prototype FreeBSD build user isolation`
- `e404e2e``Prototype FreeBSD store management`
- `eb0d77c``Adapt GNU build phases for FreeBSD`
- `d47dc9b``Prototype FreeBSD package definitions`
- `b36746f``Validate Shepherd services on FreeBSD`
Next recommended step:
1. complete Phase 4.3 by adding a small FreeBSD Shepherd bridge layer for rc.d-style services, loopback/network configuration, filesystem setup, and temporary user/group administration
2. use that bridge layer in a runnable integration harness that validates both activation and cleanup of those FreeBSD concepts
3. continue carrying the separate real-checkout runtime blocker for later integration work:
- investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
## 2026-04-01 — Phase 4.3 completed: FreeBSD Shepherd bridge layer validated across rc.d, network, filesystem, and account management
Completed work:
- added a reusable FreeBSD Shepherd bridge module:
- `modules/fruix/shepherd/freebsd.scm`
- added a runnable integration harness exercising that bridge layer:
- `tests/shepherd/run-freebsd-shepherd-bridge-prototype.sh`
- wrote the Phase 4.3 report:
- `docs/reports/phase4-freebsd-shepherd-bridge.md`
- ran the bridge prototype successfully and captured metadata under:
- `/tmp/freebsd-shepherd-bridge-metadata.txt`
Important findings:
- the new module now exports concrete helper constructors for four FreeBSD integration categories:
- `freebsd-rc-service`
- `freebsd-loopback-alias-service`
- `freebsd-tmpfs-service`
- `freebsd-user-group-service`
- the integration harness used those helpers to manage a real chained host-side service graph under Shepherd covering:
- a temporary rc.d script in `/usr/local/etc/rc.d/`
- loopback alias configuration on `lo0`
- tmpfs mount/unmount with mode validation
- temporary user/group creation and removal via `pw`
- observed activation metadata confirmed all of those operations succeeded under Shepherd control:
- `target_running=yes`
- `rc_started=yes`
- `alias_present=yes`
- `tmpfs_mounted=yes`
- `tmpfs_mode=drwxr-x---`
- `user_present=yes`
- `group_present=yes`
- observed cleanup metadata confirmed that `stop root` also reversed all of those host-side effects successfully:
- `rc_stopped=yes`
- `alias_removed=yes`
- `tmpfs_unmounted=yes`
- `user_removed=yes`
- `group_removed=yes`
- the same expected FreeBSD runtime note remained true here as well:
- `System lacks support for 'signalfd'; using fallback mechanism.`
and again it did not prevent the prototype from working correctly
Current assessment:
- Phase 4.3 is now satisfied on the current prototype track
- Shepherd now has a concrete FreeBSD bridge layer in-repo rather than only ad hoc validation scripts
- with service supervision, rc.d integration, and FreeBSD host-concept bridging now all validated, Phase 4 is complete on the current FreeBSD amd64 prototype path
## 2026-04-01 — Phase 4 completed on the current FreeBSD prototype track
Phase 4 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 4.1** success criteria were met on the prototype track:
- Shepherd built successfully on FreeBSD with the fixed local Guile stack
- regular multi-service supervision worked
- dependency handling, status monitoring, privilege-aware execution, and respawn behavior were all validated
- **Phase 4.2** success criteria were met in init-integration prototype form:
- a real FreeBSD `rc.d` wrapper launched Shepherd successfully
- a minimal essential-service graph started automatically in correct dependency order
- orderly reverse shutdown through native FreeBSD service entry points was validated
- **Phase 4.3** success criteria were met on the prototype track:
- a reusable FreeBSD Shepherd bridge layer was added
- Shepherd services successfully bridged to rc.d service control, loopback/network configuration, filesystem mounting/permissions, and temporary user/group administration
- both activation and cleanup were validated
Important scope note:
- this completes the **Shepherd porting milestone** on the current prototype track, not a literal replacement of `/sbin/init` on the live host
- however, the core Shepherd questions have now been answered positively on FreeBSD:
- it builds
- it runs
- it supervises services
- it integrates with FreeBSD service-management conventions
- it can express concrete FreeBSD host-management tasks through Shepherd services
- the separate real-Guix-checkout runtime blocker still exists for later integration work:
- `./pre-inst-env guix --version` fails with `Wrong type to apply: #<syntax-transformer leave-on-EPIPE>`
but that is now clearly outside the scope of the completed Phase 4 Shepherd milestone
Recent commits:
- `e380e88``Add FreeBSD Guile verification harness`
- `cd721b1``Update progress after Guile verification`
- `27916cb``Diagnose Guile subprocess crash on FreeBSD`
- `02f7a7f``Validate local Guile fix on FreeBSD`
- `4aebea4``Add native GNU Hello FreeBSD build harness`
- `c944cdb``Validate Guix builder phases on FreeBSD`
- `0a2e48e``Validate GNU which builder phases on FreeBSD`
- `245a47d``Document gaps to real Guix FreeBSD builds`
- `d62e9b0``Investigate Guix derivation generation on FreeBSD`
- `c0a85ed``Build local Guile-GnuTLS on FreeBSD`
- `15b9037``Build local Guile-Git on FreeBSD`
- `47d31e8``Build local Guile-JSON on FreeBSD`
- `d82195b``Advance Guix checkout on FreeBSD`
- `9bf3d30``Document FreeBSD syscall mapping`
- `7621798``Prototype FreeBSD jail build isolation`
- `d65b2af``Prototype FreeBSD build user isolation`
- `e404e2e``Prototype FreeBSD store management`
- `eb0d77c``Adapt GNU build phases for FreeBSD`
- `d47dc9b``Prototype FreeBSD package definitions`
- `b36746f``Validate Shepherd services on FreeBSD`
- `83715f0``Prototype Shepherd rc.d integration`
Next recommended step:
1. return to the remaining real Guix checkout/runtime blocker and investigate the `leave-on-EPIPE` failure in `./pre-inst-env guix --version`
2. begin the next post-Phase-4 integration milestone by connecting the now-validated daemon/build/store/Shepherd prototypes more directly to real Guix checkout behavior on FreeBSD
3. continue using `/frx/store` rather than `/gnu/store` whenever future integration experiments need a persistent store root
## 2026-04-01 — Planning update: Fruix naming policy clarified for post-Phase-4 work
Completed work:
- added a new post-Phase-4 planning document:
- `docs/PLAN_2.md`
- updated that plan to clarify the naming policy for the fork going forward
Key planning decision:
- **Fruix** is now the intended user-facing product identity
- the user-facing CLI should become:
- `fruix`
- `/frx` remains the canonical store/state/config root on the FreeBSD path
- however, the plan explicitly avoids a blanket rename of all upstream-derived internal identifiers
- in particular:
- internal `guix` namespaces may remain temporarily where needed for compatibility and maintenance
- `gnu` names are preserved where they refer to real GNU concepts or components such as GNU packages, GNU Shepherd, or `gnu-build-system`
- new fork-specific modules and user-facing surfaces should prefer `fruix` naming
Current assessment:
- the naming direction is now clearer for the next integration batch
- Phase 5 and beyond should aim to:
- first make the upstream-derived checkout runnable on FreeBSD,
- then introduce a deliberate `fruix` command boundary,
- rather than destabilizing the codebase with a whole-tree `guix`/`gnu` rename too early
## 2026-04-01 — Phase 5.1 completed: checkout runtime unblocked and first `fruix` frontend boundary established
Completed work:
- added a reusable phase-5 checkout setup helper:
- `tests/guix/setup-phase5-checkout.sh`
- added a checkout runtime patch queue for the upstream-derived source tree:
- `tests/guix/patches/phase5-checkout-runtime.patch`
- added a FreeBSD daemon/build patch queue needed for later phase-5 work:
- `tests/guix/patches/phase5-guix-daemon-freebsd.patch`
- added a runtime validation harness:
- `tests/guix/run-phase5-checkout-runtime.sh`
- wrote the Phase 5.1 report:
- `docs/reports/phase5-checkout-runtime-freebsd.md`
- ran the runtime harness successfully and captured metadata under:
- `/tmp/phase5-runtime-metadata.txt`
Important findings:
- the earlier checkout blocker
- `./pre-inst-env guix --version`
- `Wrong type to apply: #<syntax-transformer leave-on-EPIPE>`
is now explained by top-level definition ordering in `guix/ui.scm`:
- `show-version-and-exit` called `leave-on-EPIPE` before the syntax transformer was defined later in the file
- on this FreeBSD path, that became a runtime application of a syntax-transformer object instead of a macro expansion site
- the phase-5 runtime patch fixes this by:
- making `(guix ui)` explicitly non-declarative
- rewriting `show-version-and-exit` to use direct `catch 'system-error` handling
- parameterizing `program-name` in `guix-main`
- deriving the top-level version banner name from `program-name`
- making `(guix scripts repl)` explicitly non-declarative as well
- the checkout now successfully runs the following commands on FreeBSD:
- `./pre-inst-env guix --version`
- `./pre-inst-env guix repl --help`
- `./pre-inst-env guix build --help`
- the first user-facing Fruix command boundary is now implemented in the checkout setup via:
- `scripts/fruix`
as a front-end alias next to `scripts/guix`
- observed runtime metadata confirmed:
- `first_guix_version_line=guix (GNU Guix) ...`
- `first_fruix_version_line=fruix (GNU Guix) ...`
- this matches the agreed naming policy:
- Fruix at the user-facing boundary
- stable upstream-derived internal `guix`/`gnu` names unless there is a concrete reason to rename them
Current assessment:
- Phase 5.1 is now satisfied on the current FreeBSD prototype track
- the key boundary has shifted from “the checkout still crashes immediately” to “the checkout runs, and can now be used as the basis for real derivation/store experiments”
- the next step is to prove that a real derivation can be emitted against `/frx/store` from the now-runnable checkout
## 2026-04-01 — Phase 5.2 completed: real derivation generation validated against `/frx/store`
Completed work:
- added a runnable derivation-generation harness:
- `tests/guix/run-phase5-derivation-generation.sh`
- wrote the Phase 5.2 report:
- `docs/reports/phase5-derivation-generation-freebsd.md`
- ran the derivation-generation harness successfully and captured metadata under:
- `/tmp/phase5-derivation-metadata.txt`
Important findings:
- the now-runnable checkout can successfully use a real daemon/store connection on FreeBSD to lower a package through:
- `package->bag`
- `bag->derivation`
- the emitted derivation is a real `/frx/store` derivation path rather than an ad hoc placeholder or shell metadata artifact
- the validation used a deliberately minimal custom package with a custom low-level build system so that this subphase isolates the real lowering/store boundary without being dominated by still-unresolved upstream bootstrap assumptions for full native FreeBSD package graphs
- observed metadata confirmed:
- `bag_name=phase5-freebsd-lowering-0`
- `bag_host_inputs=("source")`
- `drv_path=/frx/store/...-phase5-freebsd-lowering-0.drv`
- `out_path=/frx/store/...-phase5-freebsd-lowering-0`
- this means the key architectural step is now real and no longer hypothetical:
- a package object in the checkout can be lowered to a real derivation targeting `/frx/store` on FreeBSD
Current assessment:
- Phase 5.2 is now satisfied on the current FreeBSD prototype track
- the next step is no longer “can we emit a derivation at all?” but “can the same daemon/store path accept and execute a derivation-backed build request successfully?”
## 2026-04-01 — Phase 5.3 completed: minimal daemon/store RPC integration validated on FreeBSD
Completed work:
- added a runnable daemon/store RPC validation harness:
- `tests/guix/run-phase5-daemon-rpc.sh`
- wrote the Phase 5.3 report:
- `docs/reports/phase5-daemon-rpc-freebsd.md`
- ran the daemon/store RPC harness successfully and captured metadata under:
- `/tmp/phase5-daemon-rpc-metadata.txt`
Important findings:
- the patched checkout can now contact a real FreeBSD-aware daemon over a Unix socket and submit a derivation-backed build request successfully
- the resulting build path is a real `/frx/store` output rather than a simulated prototype artifact
- the successful metadata path now includes the full minimal chain needed for later system work:
- checkout command path
- daemon RPC
- derivation submission
- build execution
- store output materialization
- observed metadata confirmed:
- `drv_path=/frx/store/...-phase5-freebsd-daemon-build-0.drv`
- `out_path=/frx/store/...-phase5-freebsd-daemon-build-0`
- `payload=phase5-daemon-build-source`
- `source_path=/frx/store/...-phase5-source.txt`
- this step was exercised through the Fruix-facing checkout boundary:
- `./pre-inst-env fruix repl -- ...`
which means the user-facing transition is now connected to actual daemon/store activity, not just to help text or version banners
Current assessment:
- Phase 5.3 is now satisfied on the current FreeBSD prototype track
- the project now has a real but narrow end-to-end host-side execution path on FreeBSD:
- runnable checkout
- Fruix front-end boundary
- real derivation emission
- daemon/store RPC
- successful derivation-backed build into `/frx/store`
## 2026-04-01 — Phase 5 completed on the current FreeBSD prototype track
Phase 5 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 5.1** success criteria were met on the prototype track:
- the checkout runtime blocker around `leave-on-EPIPE` was root-caused and fixed in the patch queue
- the uninstalled checkout command path now runs on FreeBSD
- a first user-facing `fruix` command boundary was established
- **Phase 5.2** success criteria were met on the prototype track:
- a real package object was lowered through `package->bag` and `bag->derivation`
- a real derivation was emitted targeting `/frx/store`
- **Phase 5.3** success criteria were met on the prototype track:
- a real checkout command path contacted a FreeBSD-aware daemon/store path
- that path accepted and executed a derivation-backed build request
- the resulting output was materialized successfully in `/frx/store`
Important scope note:
- this completes the **real checkout and host runtime unblocking milestone** on the current prototype track, not full upstream-package-graph support for arbitrary native FreeBSD package builds yet
- the successful derivation/build path currently uses a deliberately minimal custom package/build-system path to isolate real daemon/store viability from still-unresolved upstream bootstrap and package-graph assumptions for native FreeBSD
- nevertheless, the core Phase 5 question has now been answered positively:
- the checkout runs
- real derivations can be emitted
- the daemon can be built far enough to serve store RPC
- and derivation-backed builds can succeed into `/frx/store`
Next recommended step:
1. begin Phase 6.1 by moving from the minimal custom derivation-backed package path to at least one real FreeBSD store-backed package build driven by Fruix/Guix mechanisms
2. then integrate the already validated jail/build-user model more directly into the live daemon build path
3. continue preserving the selective Fruix naming policy:
- Fruix at the product boundary
- `/frx` as the canonical store root
- stable upstream-derived internal names unless there is strong architectural value in renaming them
## 2026-04-01 — Phase 6.1 completed: real package build validated into `/frx/store`
Completed work:
- added a runnable real-package harness:
- `tests/guix/run-phase6-real-package-build.sh`
- wrote the Phase 6.1 report:
- `docs/reports/phase6-real-package-build-freebsd.md`
- ran the real-package harness successfully and captured metadata under:
- `/tmp/phase6-real-package-metadata.txt`
Important findings:
- the checkout can now build a real package definition derived from Guix's `hello` package through:
- `./pre-inst-env fruix build -f ...`
- the FreeBSD-aware daemon
- `/frx/store`
- this moves the project beyond the deliberately minimal Phase 5 custom derivation path and into a real package-definition flow
- the current successful path still uses a prefetched local GNU Hello tarball as the package source because the built-in downloader path remains a separate unresolved FreeBSD/root-daemon issue
- observed metadata confirmed:
- `drv_path=/frx/store/...-hello-2.12.3.drv`
- `out_path=/frx/store/...-hello-2.12.3`
- `source_store_path=/frx/store/...-hello-2.12.3.tar.gz`
- `runtime_output=Hello, world!`
- a daemon-side references query confirmed that the built output preserved the declared source store item as a direct reference
Current assessment:
- Phase 6.1 is now satisfied on the current FreeBSD prototype track
- the next step is to move the already validated jail/build-user model into this live package-build path rather than keeping it prototype-only
## 2026-04-01 — Phase 6.2 completed: jail/build-user isolation integrated into the real package path
Completed work:
- added a reusable UID/GID drop helper source:
- `tests/daemon/freebsd-drop-exec.c`
- added a runnable jail-integrated package harness:
- `tests/guix/run-phase6-jail-package-build.sh`
- wrote the Phase 6.2 report:
- `docs/reports/phase6-jail-build-integration-freebsd.md`
- ran the jail-integrated harness successfully and captured metadata under:
- `/tmp/phase6-jail-package-metadata.txt`
Important findings:
- a real package build derived from Guix's `hello` definition now runs through the live daemon path inside a FreeBSD jail rather than only through the earlier prototype scripts
- the actual build work inside the jail runs as dropped credentials:
- UID `35001`
- GID `35001`
- the integrated build path required one additional FreeBSD-specific adjustment beyond the earlier prototype:
- the daemon-side host `TMPDIR` path was not automatically valid inside the jail, so the jailed build environment must reset `TMPDIR=/tmp`
- observed metadata confirmed:
- `drv_path=/frx/store/...-hello-2.12.3.drv`
- `out_path=/frx/store/...-hello-2.12.3`
- `runtime_output=Hello, world!`
- `build_uid=35001`
- `build_gid=35001`
- `jail_hostname=fruix-phase6-hello-...`
- `build_mode=freebsd-jail`
- `source_store_path=/frx/store/...-hello-2.12.3.tar.gz`
- the GNU Hello test suite also passed inside the jail-integrated build path
Current assessment:
- Phase 6.2 is now satisfied on the current FreeBSD prototype track
- the next step is to validate a minimal user-facing profile installation flow on top of these real store outputs
## 2026-04-01 — Phase 6.3 completed: minimal profile installation validated on real store outputs
Completed work:
- added a runnable real-store profile harness:
- `tests/packages/run-phase6-real-store-profile-prototype.sh`
- wrote the Phase 6.3 report:
- `docs/reports/phase6-real-store-profile-freebsd.md`
- updated the Phase 6 package-build harnesses so they can recover derivation paths even when the requested outputs are already present in `/frx/store`
- ran the real-store profile harness successfully and captured metadata under:
- `/tmp/phase6-real-store-profile-metadata.txt`
Important findings:
- the current FreeBSD track now has a minimal user-facing profile installation flow built on top of real Phase 6 store outputs rather than the earlier Phase 3 package/profile prototype inputs
- the validated transaction semantics are intentionally small but real:
- generation 1 is created from the Phase 6.1 host-built store item
- generation 2 is created from the Phase 6.2 jail-built store item
- the `profile` symlink switches to generation 2
- both generations remain addressable
- observed metadata confirmed:
- `profile_target=profile-2-link`
- `generation1_store_path=/frx/store/...-hello-2.12.3`
- `generation2_store_path=/frx/store/...-hello-2.12.3`
- `current_store_path=/frx/store/...-hello-2.12.3`
- `profile_hello_output=Hello, world!`
- `clean_env_hello_output=Hello, world!`
- the upstream-derived profile layer is still not fully usable on this FreeBSD track because the current `guix profiles` / `fruix package` path still reaches the unresolved bootstrap-platform blocker:
- `dynamic linker name not known for this system "x86_64-freebsd15.0"`
- despite that blocker, the minimal Fruix-owned profile path is now validated on top of real daemon-built store items
Current assessment:
- Phase 6.3 is now satisfied on the current FreeBSD prototype track
- Phase 6 as a whole is now complete on the active FreeBSD amd64 prototype path
## 2026-04-01 — Phase 6 completed on the current FreeBSD prototype track
Phase 6 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 6.1** success criteria were met on the prototype track:
- a real package definition derived from Guix's `hello` package now builds through `fruix build`
- the output lands in `/frx/store`
- the package runs from the store and preserves a declared source reference
- **Phase 6.2** success criteria were met on the prototype track:
- a real package build now executes inside a FreeBSD jail
- the build work runs under dropped numeric build credentials
- the jailed build succeeds into `/frx/store`
- **Phase 6.3** success criteria were met on the prototype track:
- real Phase 6 store outputs can be installed into a minimal profile environment
- generation switching works in a concrete form
- package execution through the profile succeeds for the current user
Important scope note:
- this completes the **real FreeBSD-backed store-build milestone** on the current prototype track, not full upstream-package-graph support or full upstream profile-layer parity yet
- the current package path still relies on a prefetched local source tarball for GNU Hello because the built-in downloader/root-daemon path remains a separate FreeBSD issue
- the current profile-installation path is a Fruix-owned minimal layer over real store outputs because the upstream-derived profile code still hits the unresolved FreeBSD bootstrap-platform mapping blocker
- nevertheless, the core Phase 6 question has now been answered positively:
- real package definitions can be built into `/frx/store`
- those builds can run under integrated jail/build-user isolation
- and the resulting store items can be exposed through a minimal user-facing profile flow
Next recommended step:
1. begin Phase 7.1 by defining a minimal Fruix operating-system model for FreeBSD
2. carry forward the selective Fruix naming policy:
- Fruix at the product boundary
- `/frx` as the canonical store root
- stable upstream-derived internal names unless there is strong architectural value in renaming them
3. keep the two remaining Phase 6 follow-up blockers visible but scoped:
- built-in downloader/root-daemon integration for real package origins
- upstream-derived profile/bootstrap-platform support for `x86_64-freebsd15.0`
## 2026-04-01 — Phase 7.1 completed: minimal Fruix operating-system model defined for FreeBSD
Completed work:
- added the first Fruix-owned FreeBSD system module:
- `modules/fruix/system/freebsd.scm`
- added the Phase 7.1 operating-system example and validation harnesses:
- `tests/system/phase7-minimal-operating-system.scm`
- `tests/system/validate-phase7-operating-system.scm`
- `tests/system/run-phase7-operating-system-model.sh`
- extended the FreeBSD package model with additional system-oriented prototype packages:
- `freebsd-bootloader`
- `freebsd-rc-scripts`
- `freebsd-runtime`
- `%freebsd-system-packages`
- wrote the Phase 7.1 report:
- `docs/reports/phase7-operating-system-model-freebsd.md`
- ran the operating-system model harness successfully and captured metadata under:
- `/tmp/phase7-os-model-metadata.txt`
Important findings:
- the FreeBSD track now has a concrete declarative operating-system object rather than only package/profile and service prototypes
- the model currently covers:
- host identity
- kernel and bootloader assets
- essential base packages
- users/groups
- file systems
- generated `/etc` payloads
- activation payload generation
- generated Shepherd configuration
- the selected first init strategy is now explicit in the model:
- `freebsd-init+rc.d-shepherd`
- observed metadata confirmed:
- `host_name=fruix-freebsd`
- `kernel_package=freebsd-kernel`
- `bootloader_package=freebsd-bootloader`
- `base_packages=freebsd-runtime,freebsd-userland,freebsd-libc,freebsd-rc-scripts,freebsd-sh,freebsd-bash`
- `users=root,operator`
- `groups=wheel,operator`
- `generated_files=boot/loader.conf,etc/rc.conf,etc/fstab,etc/hosts,etc/passwd,etc/group,etc/shells,etc/motd,activate,shepherd/init.scm`
- `init_mode=freebsd-init+rc.d-shepherd`
Current assessment:
- Phase 7.1 is now satisfied on the current FreeBSD prototype track
- the next step is to materialize this operating-system description into a reproducible system closure under `/frx/store`
## 2026-04-01 — Phase 7.2 completed: minimal system closure generated under `/frx/store`
Completed work:
- added the Phase 7.2 closure materialization harnesses:
- `tests/system/materialize-phase7-system-closure.scm`
- `tests/system/run-phase7-system-closure.sh`
- refined the minimal operating-system example so the generated system profile also contains `/bin/sh`
- wrote the Phase 7.2 report:
- `docs/reports/phase7-system-closure-freebsd.md`
- ran the system-closure harness successfully and captured metadata under:
- `/tmp/phase7-system-closure-metadata.txt`
Important findings:
- the declarative FreeBSD Fruix operating-system object now materializes into a real system closure under `/frx/store`
- that closure contains:
- boot assets
- a merged system profile
- generated `/etc` files
- a generated activation script
- a generated Shepherd configuration
- a generated `rc.d` launcher for Shepherd
- the closure now embeds the concrete first init integration choice for the FreeBSD track:
- `freebsd-init+rc.d-shepherd`
- rerunning the same materialization produced the same closure path, which is the current prototype proof of reproducible closure generation for this phase
- observed metadata confirmed:
- `closure_path=/frx/store/...-fruix-system-fruix-freebsd`
- `closure_rebuild_path=/frx/store/...-fruix-system-fruix-freebsd`
- `kernel_store=/frx/store/...-freebsd-kernel-15.0-STABLE`
- `bootloader_store=/frx/store/...-freebsd-bootloader-15.0-STABLE`
- `guile_store=/frx/store/...-fruix-guile-runtime-3.0`
- `guile_extra_store=/frx/store/...-fruix-guile-extra-3.0`
- `shepherd_store=/frx/store/...-fruix-shepherd-runtime-1.0.9`
- `profile_bin_sh=/frx/store/...-fruix-system-fruix-freebsd/profile/bin/sh`
- `profile_sbin_init=/frx/store/...-fruix-system-fruix-freebsd/profile/sbin/init`
- `profile_rc=/frx/store/...-fruix-system-fruix-freebsd/profile/etc/rc`
Current assessment:
- Phase 7.2 is now satisfied on the current FreeBSD prototype track
- the next step is to materialize and statically validate an installable root filesystem tree from this system closure
## 2026-04-01 — Phase 7.3 completed: installable rootfs tree validated from the system closure
Completed work:
- added the Phase 7.3 rootfs materialization harnesses:
- `tests/system/materialize-phase7-rootfs.scm`
- `tests/system/run-phase7-rootfs.sh`
- wrote the Phase 7.3 report:
- `docs/reports/phase7-rootfs-freebsd.md`
- ran the rootfs harness successfully and captured metadata under:
- `/tmp/phase7-rootfs-metadata.txt`
Important findings:
- the declarative Fruix FreeBSD system can now be materialized as a root filesystem tree rather than only as a store closure directory
- the rootfs uses a Guix-like anchor:
- `/run/current-system`
so that boot assets, generated configuration, and system-profile content remain tied to the declarative system closure
- static validation confirmed:
- boot asset linkage
- generated `/etc` linkage
- activation payload presence
- Shepherd `rc.d` launch integration
- declared filesystem entries
- declared user/group provisioning in the activation path
- deterministic ready-state wiring through `/var/lib/fruix/ready`
- observed metadata confirmed:
- `rootfs=/tmp/.../rootfs`
- `closure_path=/frx/store/...-fruix-system-fruix-freebsd`
- `run_current_system_target=/frx/store/...-fruix-system-fruix-freebsd`
- `activate_target=/run/current-system/activate`
- `bin_target=/run/current-system/profile/bin`
- `sbin_target=/run/current-system/profile/sbin`
- `boot_kernel_target=/run/current-system/boot/kernel`
- `boot_loader_target=/run/current-system/boot/loader`
- `boot_loader_efi_target=/run/current-system/boot/loader.efi`
- `rc_conf_target=/run/current-system/etc/rc.conf`
- `rc_script_target=/run/current-system/usr/local/etc/rc.d/fruix-shepherd`
- `ready_marker=/var/lib/fruix/ready`
- `validation_mode=static-rootfs-check`
Current assessment:
- Phase 7.3 is now satisfied on the current FreeBSD prototype track
- Phase 7 as a whole is now complete on the active FreeBSD amd64 prototype path
## 2026-04-01 — Phase 7 completed on the current FreeBSD prototype track
Phase 7 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 7.1** success criteria were met on the prototype track:
- a minimal Fruix operating-system object now exists for FreeBSD
- it evaluates into a coherent system-closure specification
- **Phase 7.2** success criteria were met on the prototype track:
- that system model now materializes into a reproducible closure under `/frx/store`
- the closure contains boot assets, generated `/etc` files, activation payloads, and Shepherd launch integration
- **Phase 7.3** success criteria were met on the prototype track:
- the closure now materializes into a concrete rootfs tree
- the resulting rootfs passes static validation for later image-construction work
Important scope note:
- this completes the **declarative system-composition milestone** for the current prototype track, not a fully booted Fruix guest yet
- the current output is a validated closure plus rootfs tree; Phase 8 still needs to turn that into a reproducible bhyve-friendly disk image
- the chosen first system-init strategy remains:
- FreeBSD init + `rc.d` launching Shepherd
rather than Shepherd-as-PID-1
- the current system model remains Fruix-owned and FreeBSD-oriented rather than attempting full upstream Guix System integration prematurely
Next recommended step:
1. begin Phase 8.1 by creating a reproducible disk-image build path from the generated Fruix rootfs tree
2. keep the current init decision explicit for the first boot target:
- FreeBSD init + `rc.d` + Shepherd
3. continue preserving the selective Fruix naming policy:
- Fruix at the product boundary
- `/frx` as the canonical store root
- stable upstream-derived internal names unless there is strong architectural value in renaming them
## 2026-04-01 — Phase 8.1 completed: reproducible bhyve-compatible raw disk image generated
Completed work:
- added the first Phase 8 image-generation harness:
- `tests/system/run-phase8-bhyve-image.sh`
- wrote the Phase 8.1 report:
- `docs/reports/phase8-bhyve-image-freebsd.md`
- ran the image-generation harness successfully and captured metadata under:
- `/tmp/phase8-bhyve-image-metadata.txt`
Important findings:
- the current Fruix FreeBSD track now has a reproducible raw disk-image build path using:
- GPT
- UEFI boot
- a FAT EFI system partition
- a UFS root partition
- serial-console-friendly loader settings
- the earlier Phase 7 rootfs tree was not sufficient by itself for an installable image because it still referenced `/frx/store` content that only existed on the host; the image builder therefore had to stage the system closure and its recursively declared store references inside the image rootfs under `/frx/store`
- rebuilding the same image a second time with fixed timestamps and explicit filesystem parameters produced the same SHA256, which is the current prototype proof of reproducible image generation on this host
- observed metadata confirmed:
- `raw_sha256=08605d738021cb6fb5b87c270e1eafde57e1acb5159d3a2257aad4c560e2efc5`
- `image_size_bytes=335578624`
- `esp_fstype=msdosfs`
- `root_fstype=ufs`
- `run_current_system_target=/frx/store/...-fruix-system-fruix-freebsd`
- `boot_loader_target=/run/current-system/boot/loader`
- `boot_loader_conf_target=/run/current-system/boot/loader.conf`
- `rc_conf_target=/run/current-system/etc/rc.conf`
- `rc_script_target=/run/current-system/usr/local/etc/rc.d/fruix-shepherd`
- `store_item_count=13`
- `boot_mode=uefi`
- `image_format=raw`
- `partition_scheme=gpt`
- `serial_console=comconsole`
Current assessment:
- Phase 8.1 is now satisfied on the current FreeBSD prototype track
- the next step is to integrate this image generation path into the declarative Fruix system-composition layer so that a single operating-system description can drive image generation end-to-end
## 2026-04-01 — Phase 8.2 completed: image generation integrated into the declarative system layer
Completed work:
- extended the FreeBSD system module with integrated image-generation operations:
- `operating-system-image-spec`
- `materialize-bhyve-image`
- added the Phase 8.2 integration harnesses:
- `tests/system/materialize-phase8-system-image.scm`
- `tests/system/run-phase8-system-image.sh`
- wrote the Phase 8.2 report:
- `docs/reports/phase8-system-image-freebsd.md`
- ran the integrated system-image harness successfully and captured metadata under:
- `/tmp/phase8-system-image-metadata.txt`
Important findings:
- image generation is now a direct output of the Fruix FreeBSD system-definition layer rather than an external shell-only follow-up to Phase 7
- the integrated path now stores the resulting image artifact itself under `/frx/store`, preserving the store-centered Fruix composition story even at the VM-image layer
- rerunning `materialize-bhyve-image` for the same operating-system description produced the same image store path, which is the current prototype proof that one declarative system object can drive image generation end-to-end
- observed metadata confirmed:
- `image_store_path=/frx/store/...-fruix-bhyve-image-fruix-freebsd`
- `disk_image=/frx/store/...-fruix-bhyve-image-fruix-freebsd/disk.img`
- `closure_path=/frx/store/...-fruix-system-fruix-freebsd`
- `raw_sha256=ac57d4c694ea3cf6b1bd24be48982090a6cfcfa301d052c1f903636a46f2d56e`
- `image_size_bytes=335578624`
- `store_item_count=13`
- `esp_fstype=msdosfs`
- `root_fstype=ufs`
- `run_current_system_target=/frx/store/...-fruix-system-fruix-freebsd`
- `boot_loader_target=/run/current-system/boot/loader`
- `rc_conf_target=/run/current-system/etc/rc.conf`
- `rc_script_target=/run/current-system/usr/local/etc/rc.d/fruix-shepherd`
- `image_generation_mode=declarative-system-layer`
Current assessment:
- Phase 8.2 is now satisfied on the current FreeBSD prototype track
- Phase 8 as a whole is now complete on the active FreeBSD amd64 prototype path
## 2026-04-01 — Phase 8 completed on the current FreeBSD prototype track
Phase 8 is now considered complete for the active FreeBSD amd64 prototype path.
Why this milestone is satisfied:
- **Phase 8.1** success criteria were met on the prototype track:
- a reproducible raw GPT+UEFI+UFS image can now be generated from the Fruix system outputs
- that image passes static boot-structure sanity checks
- **Phase 8.2** success criteria were met on the prototype track:
- the image builder is now integrated with the declarative Fruix system-definition layer
- a single operating-system description now drives image generation end-to-end
- the integrated output is itself a store-backed Fruix image artifact under `/frx/store`
Important scope note:
- this completes the **image-construction milestone** for the current prototype track, not the first successful bhyve boot yet
- the generated image is now ready for the next phases VM-launch and serial-console validation work
- the current first-boot strategy remains explicit and unchanged:
- FreeBSD init + `rc.d` + Shepherd
- the image path still reflects the current prototype system/runtime limitations, including the fact that deeper runtime closure completeness for locally copied Guile/Shepherd dependencies will be exercised more fully in Phase 9 boot validation
Next recommended step:
1. begin Phase 9.1 by creating a bhyve launcher and serial-console validation harness for the generated image
2. keep the current deterministic ready-state target visible:
- Shepherd startup leading to the generated `/var/lib/fruix/ready` marker path
3. continue preserving the selective Fruix naming policy:
- Fruix at the product boundary
- `/frx` as the canonical store root
- stable upstream-derived internal names unless there is strong architectural value in renaming them
## 2026-04-02 — Phase 9 checkpoint: XCP-ng guest reached DHCP and SSH
Completed work:
- added a dedicated Phase 9 XCP-ng operating-system template:
- `tests/system/phase9-minimal-operating-system.scm.in`
- added an XCP-ng boot/import/validation harness:
- `tests/system/run-phase9-xcpng-boot.sh`
- extended the staged FreeBSD runtime and system-generation layers so the guest can complete enough of real boot for network access:
- `modules/fruix/packages/freebsd.scm`
- `modules/fruix/system/freebsd.scm`
- updated the integrated image-generation path for Phase 9 use cases:
- `tests/system/materialize-phase8-system-image.scm`
- `tests/system/run-phase8-system-image.sh`
- wrote the checkpoint report:
- `docs/reports/phase9-xcpng-ssh-boot-freebsd.md`
Important findings:
- a decisive local QEMU/TCG serial-boot pass exposed the first real early-boot blocker:
- the generated `fstab` was wrong for pseudo-filesystems, so `rc` tried to fsck `devfs` and aborted boot
- after fixing `fstab`, later serial logs exposed additional FreeBSD base runtime gaps that only appear during real boot, including missing commands, runtime directories, and base config files used by `rc`, DHCP, logging, and service startup
- the staged image now includes the minimum currently known set of FreeBSD runtime pieces needed to:
- run `rc`
- obtain DHCP
- generate SSH host keys
- start `sshd`
- public-key SSH login initially still failed because the minimal guest did not stage a complete PAM runtime/config path; for the current Phase 9 prototype track, the generated `sshd_config` now uses:
- `UsePAM no`
- the current XCP-ng validation path succeeded against the operator-approved VM and existing VDI only:
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- the successful XCP-ng boot obtained:
- guest IP `192.168.213.62`
- successful SSH validation on the real guest confirmed:
- `hostname=fruix-freebsd`
- `sshd` is reachable with the injected root key
- networking is configured on the Xen NIC
Current assessment:
- this checkpoint establishes the first real network-reachable Fruix boot on the active FreeBSD/XCP-ng track
- the generated image now boots far enough for DHCP and SSH, which closes the earlier uncertainty about whether the Phase 8 image could become a remotely usable guest at all
- Phase 9 is still not complete because the Fruix-specific readiness path remains blocked:
- `fruix-shepherd` does not start
- `/var/lib/fruix/ready` is still missing
- Guile still crashes in the guest with `signal 11`
- therefore the current state is:
- kernel boot: yes
- root mount: yes
- DHCP: yes
- SSH: yes
- Shepherd/ready marker: not yet
Next recommended step:
1. continue the in-guest Guile crash investigation so `fruix-shepherd` can start on the booted guest
2. once Shepherd is stable, rerun `tests/system/run-phase9-xcpng-boot.sh` to validate the full ready-marker path end-to-end
3. then close Phase 9 with updated report/progress entries for:
- deterministic boot readiness
- in-guest Shepherd validation
- minimal operator usability
## 2026-04-02 — Phase 9 completed on the active XCP-ng FreeBSD track
Completed work:
- resolved the in-guest Guile/Shepherd blocker that remained after the earlier DHCP+SSH checkpoint
- wrote the completion report:
- `docs/reports/phase9-xcpng-ready-boot-freebsd.md`
- extended the staged runtime again in:
- `modules/fruix/packages/freebsd.scm`
- added `/usr/sbin/daemon`
- added `/usr/share/locale/C.UTF-8/LC_CTYPE`
- completed the guest runtime integration in:
- `modules/fruix/system/freebsd.scm`
- activation now recreates compatibility symlinks for the currently locally built Guile / guile-extra / Shepherd prefixes, but points them at the real `/frx/store` items in the guest
- the rootfs now exposes `/usr/share/locale`
- the generated Shepherd config no longer relies on missing `mkdir-p` or unsupported `call-with-output-file #:append` behavior
- the Shepherd rc script now exports `LANG=C.UTF-8` / `LC_ALL=C.UTF-8`
- the Shepherd rc script now exports explicit Guile system/site path variables
- Shepherd is now started through FreeBSD `daemon(8)` so it remains alive after rc/session teardown
- corrected the XCP-ng harness in:
- `tests/system/run-phase9-xcpng-boot.sh`
- it now uses a distinct SSH private key file for login instead of incorrectly trying to authenticate with the public key file
Important findings:
- the original guest Guile failure had multiple layers:
- missing UTF-8 locale data in the image
- baked-in temporary install-prefix references inside the copied Guile / guile-extra / Shepherd artifacts
- and Shepherd process lifetime issues caused by a fragile shell-background startup path
- reproducing the problem in a host-side chroot into the generated image root partition made the final debugging loop much tighter than repeated full VM imports alone
- after locale staging and compatibility-prefix recreation, Guile and Shepherd became runnable in the guest, but Shepherd still exited too early on the real boot path until it was launched via `daemon(8)`
- after those fixes, the full ready-marker path became reliable enough for end-to-end XCP-ng validation
Final validation:
- `tests/system/run-phase9-xcpng-boot.sh` now passes end-to-end against:
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- passing run workdir:
- `/tmp/phase9-xcpng-pass-1775113189`
- passing real-guest metadata confirmed:
- `ready_marker=ready`
- `shepherd_status=running`
- `sshd_status=running`
- `run_current_system_target=/frx/store/0fe459ea22156510e64cea794b7a001151b59625bd5f12a488d6851e1c6d2198-fruix-system-fruix-freebsd`
- `operator_home_listing=/home/operator`
- `logger_log=fruix-shepherd-started`
Current assessment:
- Phase 9 is now complete for the active FreeBSD prototype track, using the XCP-ng replacement path adopted for this environment
- the generated Fruix image now reaches all currently required first-boot milestones on the real VM:
- kernel boot: yes
- root mount: yes
- DHCP: yes
- SSH: yes
- Shepherd: yes
- ready marker: yes
- minimal operator usability: yes
- this establishes the first real Fruix-on-FreeBSD VM that:
- boots from the declaratively generated image,
- reaches the generated ready state,
- keeps Shepherd running,
- and remains inspectable over SSH as a minimally usable system
Next recommended step:
1. begin the next post-Phase-9 cleanup/native-integration pass from `docs/PLAN_2.md` Optional Phase 10
2. prioritize replacing the current compatibility shims for locally built Guile / Shepherd prefixes with a more native store-path-aware Fruix runtime arrangement
3. clean up remaining non-fatal boot noise observed during Phase 9, such as:
- login-class warnings around `daemon`
- the `gpart: Unknown command: show` rc noise
- residual syslog/cron/runtime polish issues where they still matter
## 2026-04-02 — Phase 10.1: added a real `fruix system` command
Completed work:
- started Optional Phase 10 with the first user-facing Fruix system-management command surface
- wrote the subphase report:
- `docs/reports/phase10-fruix-system-command-freebsd.md`
- added a real CLI wrapper:
- `bin/fruix`
- added the corresponding Guile entry point:
- `scripts/fruix.scm`
- added a dedicated validation harness:
- `tests/system/run-phase10-fruix-system-command.sh`
What the new command does:
- exposes declarative FreeBSD system artifact generation through a Fruix CLI instead of only phase-specific harness scripts
- currently supports:
- `fruix system build OS-FILE`
- `fruix system image OS-FILE`
- `fruix system rootfs OS-FILE ROOTFS-DIR`
- currently supports options:
- `--system NAME`
- `--store DIR`
- `--disk-capacity SIZE`
- `--rootfs DIR`
- `--help`
- loads an operating-system object from a Scheme file, validates it, and dispatches to:
- `materialize-operating-system`
- `materialize-rootfs`
- `materialize-bhyve-image`
- emits machine-readable `key=value` metadata for the produced artifacts
Important findings:
- the project already had the core declarative system machinery by the end of Phase 9; the missing piece here was a direct Fruix operator interface to that machinery
- a small dedicated wrapper in this repo is currently the most practical way to expose that functionality without waiting for deeper upstream command-framework integration
- keeping the command aligned with the already validated local FreeBSD Guile / Fibers / Shepherd toolchain avoids introducing a second, divergent runtime path
Validation:
- `tests/system/run-phase10-fruix-system-command.sh` passes
- passing run workdir:
- `/tmp/phase10-fruix-cmd-1775117490`
- the test validated that:
- `fruix system build` materializes a closure under `/frx/store/*-fruix-system-fruix-freebsd`
- the produced closure contains the activation path
- `fruix system image` materializes a disk image under `/frx/store/*-fruix-bhyve-image-fruix-freebsd/disk.img`
- the command returns expected metadata fields for both actions
Current assessment:
- Fruix now has a real user-facing system command in this repo, which is a concrete step from “prototype scripts” toward “OS tooling”
- this does not replace all earlier harnesses yet, but it establishes `fruix system` as the new canonical interface for system closure/rootfs/image materialization work
- system/image creation still typically requires `sudo` because the command writes into `/frx/store` and uses privileged image-building operations
Next recommended step:
1. continue Phase 10 by making more of the existing system workflows call `bin/fruix` directly instead of bespoke phase scripts
2. reduce the current runtime compatibility shims for locally built Guile / Shepherd prefixes and move toward a more native store-path-aware Fruix runtime arrangement
3. consider adding the next operator-facing subcommand on top of the now-working image path, such as a `vm`/deploy-oriented flow for the active XCP-ng workflow
## 2026-04-02 — Phase 10 completed: canonical system workflows now use `fruix system`
Completed work:
- completed the current Optional Phase 10 track by making the existing FreeBSD system workflows use the real Fruix CLI as their canonical frontend
- wrote the completion report:
- `docs/reports/phase10-canonical-system-workflows-freebsd.md`
- extended the `fruix system build` metadata in:
- `scripts/fruix.scm`
- added:
- `ready_marker`
- `base_package_store_count`
- `base_package_stores`
- refactored the main static system harnesses to call `bin/fruix` instead of invoking internal Scheme materializer runners directly:
- `tests/system/run-phase7-system-closure.sh`
- `tests/system/run-phase7-rootfs.sh`
- `tests/system/run-phase8-system-image.sh`
What changed in practice:
- the closure path is now validated through:
- `fruix system build`
- the rootfs path is now validated through:
- `fruix system rootfs`
- the image path is now validated through:
- `fruix system image`
- the real XCP-ng boot path now benefits from this automatically because:
- `tests/system/run-phase9-xcpng-boot.sh`
still calls the Phase 8 harness, and that harness now builds its image through `bin/fruix`
Validation:
- `tests/system/run-phase7-system-closure.sh` passes
- workdir: `/tmp/phase10-canon-closure2-1775119728`
- `tests/system/run-phase7-rootfs.sh` passes
- workdir: `/tmp/phase10-canon-rootfs3-1775120391`
- `tests/system/run-phase8-system-image.sh` passes
- workdir: `/tmp/phase10-canon-image-1775120548`
- full real XCP-ng regression still passes after the frontend refactor:
- `tests/system/run-phase9-xcpng-boot.sh`
- workdir: `/tmp/phase10-canon-xcpng-1775120869`
Important findings:
- the Phase 9 booted system path was already real, but the remaining transitional layer was the tooling/frontend boundary rather than the system internals
- adding `bin/fruix` in Phase 10.1 was necessary but not sufficient on its own; the existing validation and deployment workflows also had to adopt it, or the project would still effectively be driven by bespoke phase scripts
- after this refactor, the command path is now exercised by:
- static closure validation
- static rootfs validation
- static image validation
- and the real XCP-ng boot/import/SSH/ready-marker path
Current assessment:
- Optional Phase 10 is now complete for the current FreeBSD prototype track
- Fruix now has:
- a user-facing command surface in `bin/fruix`
- real `system build`, `system rootfs`, and `system image` actions
- canonical validation/deployment workflows that use that command instead of directly entering the materializers
- and a command-driven image path that remains validated on the real XCP-ng VM
- this means the project now has not just declarative OS internals, but also a real Fruix operator/tooling layer around those internals
Next recommended step:
1. begin the next post-Phase-10 cleanup/polish pass outside the plan milestones
2. prioritize replacing the current Guile / Shepherd compatibility-prefix shims with a more native store-path-aware runtime arrangement
3. consider adding richer deploy/vm-oriented `fruix` commands beyond the now-canonical `system build/rootfs/image` path
## 2026-04-02 — Post-Phase-10: local Shepherd-as-PID-1 prototype booted on FreeBSD
Completed work:
- began the next post-Phase-10 runtime-integration pass by exploring a Shepherd-as-PID-1 boot mode for Fruix on FreeBSD
- compared the approach with Guix's root Shepherd design in:
- `~/repos/guix/gnu/services/shepherd.scm`
- wrote the subphase report:
- `docs/reports/postphase10-shepherd-pid1-qemu-freebsd.md`
- extended the declarative FreeBSD operating-system model in:
- `modules/fruix/system/freebsd.scm`
- added an `init-mode` field with:
- `freebsd-init+rc.d-shepherd`
- `shepherd-pid1`
- generated loader configuration now sets:
- `init_exec="/run/current-system/boot/fruix-pid1"`
when `init-mode` is `shepherd-pid1`
- generated systems in PID 1 mode now include:
- `boot/fruix-pid1`
- the generated activation script now treats `cap_mkdb` / `pwd_mkdb` as best-effort so immutable store-backed config files do not abort this early boot path
- added a dedicated Shepherd-PID-1 operating-system template:
- `tests/system/phase11-shepherd-pid1-operating-system.scm.in`
- added a dedicated local QEMU/UEFI validation harness:
- `tests/system/run-phase11-shepherd-pid1-qemu.sh`
Important findings:
- FreeBSD's `init(8)` already has a suitable handoff mechanism for this experiment via:
- `init_exec`
- compared with Guix, the current Fruix implementation is still much more imperative, but it now follows the same broad direction:
- boot into Shepherd directly as PID 1 rather than merely starting Shepherd late from rc.d
- the first PID 1 attempt failed because the generated Shepherd config imported a repo-side module:
- `(fruix shepherd freebsd)`
that was not present inside the guest runtime; the fix was to inline the small helper procedures needed by the generated config itself
- the early PID 1 path also exposed that store-backed `/etc/login.conf` and `/etc/master.passwd` updates must be best-effort rather than fatal on this bootstrap path
- for the current locally built runtime artifacts, the compatibility-prefix shims are still needed; this subphase did not eliminate them yet, but it did remove the larger `rc.d` boot-manager dependency from the local prototype path
Validation:
- `tests/system/run-phase11-shepherd-pid1-qemu.sh` now passes
- passing run workdir:
- `/tmp/pid1-qemu6-1775128407`
- validated local guest state included:
- `ready_marker=ready`
- `shepherd_pid=1`
- `shepherd_socket=present`
- `shepherd_status=running`
- `sshd_status=running`
- `boot_backend=qemu-uefi-tcg`
- `init_mode=shepherd-pid1`
Current assessment:
- Fruix now has a working local FreeBSD prototype where Shepherd itself is PID 1
- this is not yet the new mainline boot path, but it proves that the project can move beyond the earlier `freebsd-init+rc.d-shepherd` bridge architecture
- the PID 1 process image appears as Guile because Shepherd is launched as a Guile script, but the decisive validation point is that:
- `/var/run/shepherd.pid` contains `1`
- this subphase was validated locally under QEMU/TCG + UEFI; the next meaningful test is the real XCP-ng VM
Next recommended step:
1. try the `shepherd-pid1` image on the real XCP-ng VM
2. if it boots there too, decide whether to keep `shepherd-pid1` as an experimental selectable boot mode or advance it further toward the main Fruix boot path
3. continue reducing the remaining Guile / Shepherd compatibility-prefix shims now that the broader `rc.d` boot-manager dependency has been locally bypassed
## 2026-04-02 — Post-Phase-10: Shepherd-as-PID-1 boot also passed on the real XCP-ng VM
Completed work:
- took the locally validated `shepherd-pid1` boot mode and tested it on the real XCP-ng deployment path
- wrote the follow-up report:
- `docs/reports/postphase10-shepherd-pid1-xcpng-freebsd.md`
- expanded the Shepherd-PID-1 operating-system template so the generated guest remains compatible with both local virtio and the real Xen NIC path:
- `tests/system/phase11-shepherd-pid1-operating-system.scm.in`
- now includes:
- `ifconfig_xn0=SYNCDHCP`
- `ifconfig_em0=SYNCDHCP`
- `ifconfig_vtnet0=SYNCDHCP`
- added a dedicated real-VM Shepherd-PID-1 deployment/validation harness:
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh`
Validation:
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh` now passes on the operator-approved VM and existing VDI:
- VM `90490f2e-e8fc-4b7a-388e-5c26f0157289`
- VDI `0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- passing run workdir:
- `/tmp/pid1-xcpng-1775129768`
- passing real-guest metadata confirmed:
- `ready_marker=ready`
- `run_current_system_target=/frx/store/2940c952e9d35e47f98fe62f296be2b6ab4fceb3eee8248d6a7823decd42a305-fruix-system-fruix-freebsd`
- `pid1_command=[guile]`
- `shepherd_pid=1`
- `shepherd_socket=present`
- `shepherd_status=running`
- `sshd_status=running`
- `init_mode=shepherd-pid1`
Important findings:
- the local QEMU PID 1 prototype was not a simulator-only artifact; the same general boot design also works on the real XCP-ng/Xen guest
- as expected for a Guile-script entry point, the PID 1 process image shows up as Guile, but the meaningful architectural check is that:
- `/var/run/shepherd.pid` contains `1`
- this means Fruix has now validated two distinct real-VM boot architectures on FreeBSD:
- `freebsd-init+rc.d-shepherd`
- `shepherd-pid1`
- however, this still does not remove the current Guile / Shepherd compatibility-prefix shims; those remain a separate runtime-artifact issue rather than an init-manager issue
Current assessment:
- Shepherd-as-PID-1 is now no longer merely a local prototype; it is validated on the real XCP-ng VM as well
- this significantly strengthens the path toward a more Guix-like Fruix system architecture on FreeBSD
- the main remaining native-runtime gap is now the baked-prefix / compatibility-shim problem, not whether Fruix can boot with Shepherd as PID 1
Next recommended step:
1. focus directly on eliminating the remaining Guile / Shepherd compatibility-prefix shims from the guest runtime
2. preserve `shepherd-pid1` as an experimental selectable boot mode while that cleanup proceeds
3. once the runtime-prefix issue is reduced, reassess whether `shepherd-pid1` should replace the older `freebsd-init+rc.d-shepherd` path as the preferred Fruix boot architecture
## 2026-04-02 — Post-Phase-10: removed runtime dependence on `/tmp` Guile / Shepherd compatibility-prefix shims
Completed work:
- removed the generated guest's runtime dependence on the old `/tmp` compatibility-prefix symlinks for Guile, guile-extra, and Shepherd
- wrote the subphase report:
- `docs/reports/postphase10-runtime-prefix-shims-freebsd.md`
- updated the prefix materializer in:
- `modules/fruix/system/freebsd.scm`
- bumped the prefix-materializer revision
- added deterministic post-copy sanitation for staged runtime prefixes
- removed activation-time recreation of these guest-side shims from the generated activation path:
- `/tmp/guile-freebsd-validate-install`
- `/tmp/guile-gnutls-freebsd-validate-install`
- `/tmp/shepherd-freebsd-validate-install`
- sanitized the staged guile-extra runtime so it no longer depends on those old prefixes for key module loading:
- patched `fibers/config.scm` to use `GUILE_EXTENSIONS_PATH`
- patched `gnutls.scm` to fall back to `GUILE_EXTENSIONS_PATH`
- removed stale compiled cache files that would otherwise retain the old prefix behavior:
- `fibers/config.go`
- `gnutls.go`
- sanitized the staged Shepherd runtime so it no longer depends on the old temporary prefix for `shepherd config`:
- patched `share/guile/site/3.0/shepherd/config.scm`
- removed stale compiled cache file:
- `shepherd/config.go`
- extended the real XCP-ng validation harnesses so they now explicitly check for:
- absence of the `/tmp` compatibility-prefix trees
- successful Guile module loading from the store-backed runtime
- updated:
- `tests/system/run-phase9-xcpng-boot.sh`
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh`
Validation:
- `tests/system/run-phase9-xcpng-boot.sh` passes on the real VM with:
- workdir: `/tmp/noshim-phase9-smoke-1775143001`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `ready_marker=ready`
- `shepherd_status=running`
- `sshd_status=running`
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh` passes on the real VM with:
- workdir: `/tmp/noshim-phase11-smoke-1775142712`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `ready_marker=ready`
- `shepherd_pid=1`
- `shepherd_status=running`
- `sshd_status=running`
- a direct manual guest probe also confirmed that all three `/tmp` compatibility-prefix paths are absent while Guile can still load:
- `(fibers config)`
- `(gnutls)`
- `(shepherd config)`
Important findings:
- the remaining native-runtime problem was narrower than the earlier boot-manager issue:
- boot was already solved
- PID 1 was already solved
- the next real dependency to remove was the guest's reliance on temporary compatibility aliases
- deleting the stale compiled cache files for the affected modules was important; otherwise Guile could continue using prefix-baked compiled forms even after the source modules were patched
- this subphase removes runtime dependence on the old `/tmp` compatibility shims, but it does not yet guarantee that every embedded historical prefix string has disappeared from every binary or metadata artifact
Current assessment:
- Fruix now boots and runs from a store-backed Guile / Shepherd runtime arrangement on FreeBSD without needing guest-side `/tmp` compatibility-prefix symlinks
- this now holds for both validated real-VM boot modes:
- `freebsd-init+rc.d-shepherd`
- `shepherd-pid1`
- the main remaining cleanup is deeper and lower-level:
- move the local Guile / guile-extra / Shepherd build/install flow itself closer to a truly store-native prefix so the remaining baked strings disappear from the artifacts rather than merely becoming runtime-irrelevant
Next recommended step:
1. keep `shepherd-pid1` available as the stronger experimental boot architecture
2. start pushing the local Guile / guile-extra / Shepherd build/install process itself toward a truly store-native prefix layout
3. clean up the remaining historical prefix strings still present in binaries, libtool metadata, and pkg-config metadata where they still matter for developer/operator workflows
## 2026-04-02 — Phase 12.1: deployment provenance and diagnostic metadata improved
Completed work:
- wrote the next-stage plan document:
- `docs/PLAN_3.md`
- added a concise progress snapshot:
- `docs/PROG_SUMMARY.md`
- wrote the Phase 12.1 report:
- `docs/reports/phase12-provenance-diagnostics-freebsd.md`
- updated `modules/fruix/system/freebsd.scm` so generated system closures now carry explicit provenance files:
- `metadata/host-base-provenance.scm`
- `metadata/store-layout.scm`
- those closure metadata files now record at least:
- host `freebsd-version -kru`
- host `uname -a`
- `/usr/src` path
- `/usr/src` git revision/branch when available
- `newvers.sh` SHA256 as a fallback source-tree identifier
- exact host-staged FreeBSD base store paths
- exact Fruix runtime store paths
- selected init mode
- extended `scripts/fruix.scm` so `fruix system build` and `fruix system image` now emit explicit provenance/layout metadata including:
- `host_base_store_count`
- `host_base_stores`
- `fruix_runtime_store_count`
- `fruix_runtime_stores`
- `host_base_provenance_file`
- `store_layout_file`
- `host_freebsd_version`
- `host_uname`
- `usr_src_git_revision`
- `usr_src_git_branch`
- `usr_src_newvers_sha256`
- tightened the local closure/image validation harnesses so they now assert that this provenance metadata exists:
- `tests/system/run-phase7-system-closure.sh`
- `tests/system/run-phase8-system-image.sh`
- expanded the VM-oriented harnesses to collect more guest-side diagnostic tails where available:
- `tests/system/run-phase9-xcpng-boot.sh`
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh`
- `tests/system/run-phase11-shepherd-pid1-qemu.sh`
- now include additional capture for:
- `shepherd-bootstrap.out`
- `shepherd.log`
- recent `dmesg`
- stabilized local reproducibility checks by forcing:
- `GUILE_AUTO_COMPILE=0`
in the host-side closure/image harnesses when invoking `fruix` under `sudo env`
Validation:
- `tests/system/run-phase7-system-closure.sh` passes with the new provenance checks:
- workdir: `/tmp/phase12-1b-closure-1775157039`
- confirmed:
- `host_base_store_count=8`
- `fruix_runtime_store_count=3`
- `host_base_provenance_file=/frx/store/.../metadata/host-base-provenance.scm`
- `store_layout_file=/frx/store/.../metadata/store-layout.scm`
- `host_freebsd_version=15.0-STABLE`
- `tests/system/run-phase8-system-image.sh` passes with the new provenance checks:
- workdir: `/tmp/phase12-1b-image-1775157039`
- confirmed:
- `host_base_store_count=8`
- `fruix_runtime_store_count=3`
- `host_base_provenance_file=/frx/store/.../metadata/host-base-provenance.scm`
- `store_layout_file=/frx/store/.../metadata/store-layout.scm`
- `host_uname=FreeBSD fruixdev 15.0-STABLE ...`
- syntax-checked successfully:
- `tests/system/run-phase9-xcpng-boot.sh`
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh`
- `tests/system/run-phase11-shepherd-pid1-qemu.sh`
Important findings:
- the current host-staged FreeBSD base pipeline is still transitional, but it is now much more inspectable and self-describing
- one-shot reproducibility failures immediately after source edits were partly a host-side auto-compilation artifact; forcing `GUILE_AUTO_COMPILE=0` in the validation harnesses makes the closure/image checks more stable
- the project can now identify much more directly:
- which closure/image inputs came from the host-staged FreeBSD base
- which came from Fruix-built Guile/Shepherd runtime artifacts
- what `/usr/src` identity was available at build time
Current assessment:
- Fruix now has a better-documented and easier-to-debug working pipeline for the current host-staged FreeBSD base model
- this is the right amount of hardening before beginning native FreeBSD base-build work; it improves traceability without pretending the current host-copy model is final
Next recommended step:
1. continue with Phase 12.2 and tighten the guest-side runtime/operator diagnostics
2. remove or reduce the most distracting remaining boot/runtime rough edges where the fixes are small and local
3. keep the deployment path stable so Phase 13 can start from a sharper baseline
## 2026-04-02 — Phase 12.2: guest runtime diagnostics tightened and `/etc` handling improved
Completed work:
- wrote the Phase 12.2 report:
- `docs/reports/phase12-runtime-diagnostics-freebsd.md`
- updated `modules/fruix/system/freebsd.scm` so selected database-backed `/etc` files are now materialized as regular files in the guest rootfs instead of symlinks:
- `/etc/passwd`
- `/etc/master.passwd`
- `/etc/group`
- `/etc/login.conf`
- the generated activation script now refreshes those files from `/run/current-system/etc` before rebuilding FreeBSD databases
- activation now writes a guest-visible log:
- `/var/log/fruix-activate.log`
- with markers including:
- `fruix-activate:start`
- `fruix-activate:cap_mkdb=ok`
- `fruix-activate:pwd_mkdb=ok`
- `fruix-activate:done`
- exit status marker via shell trap
- tightened closure permissions slightly by making:
- `etc/master.passwd`
mode `0600`
- upgraded validation harnesses so they now assert the improved runtime behavior directly:
- `tests/system/run-phase8-system-image.sh`
- now checks that image `/etc/login.conf` is a regular file
- now checks that image `/etc/master.passwd` is a regular file
- `tests/system/run-phase9-xcpng-boot.sh`
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh`
- `tests/system/run-phase11-shepherd-pid1-qemu.sh`
- now check for:
- `login_conf_kind=regular`
- `login_conf_db=present`
- `pwd_dbs=present`
- activation log completion marker
- fixed a small follow-up bug in the activation log path:
- initial implementation used `touch`, which is not staged in the minimal guest
- switched to shell redirection instead:
- `: >> "$logfile"`
Validation:
- `tests/system/run-phase8-system-image.sh` passes locally with the new image-layout checks:
- workdir: `/tmp/phase12-2-image-1775159011`
- confirmed:
- `login_conf_kind=regular`
- `master_passwd_kind=regular`
- `tests/system/run-phase11-shepherd-pid1-qemu.sh` passes locally again with the new activation/runtime checks:
- workdir: `/tmp/phase12-2b-qemu-1775161367`
- confirmed:
- `activate_log=fruix-activate:start ... fruix-activate:done ...`
- `login_conf_kind=regular`
- `login_conf_db=present`
- `pwd_dbs=present`
- `shepherd_pid=1`
- `sshd_status=running`
- `tests/system/run-phase9-xcpng-boot.sh` passes on the real VM with the new checks:
- workdir: `/tmp/phase12-2b-phase9-1775161731`
- confirmed:
- `activate_log=fruix-activate:start ... fruix-activate:done ...`
- `login_conf_kind=regular`
- `login_conf_db=present`
- `pwd_dbs=present`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `shepherd_status=running`
- `sshd_status=running`
- `tests/system/run-phase11-shepherd-pid1-xcpng.sh` passes on the real VM with the new checks:
- workdir: `/tmp/phase12-2b-phase11-1775162210`
- confirmed:
- `activate_log=fruix-activate:start ... fruix-activate:done ...`
- `login_conf_kind=regular`
- `login_conf_db=present`
- `pwd_dbs=present`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `shepherd_pid=1`
- `sshd_status=running`
Important findings:
- the old symlink-based handling for login/password database inputs was a real mismatch with FreeBSD expectations; making those files regular in the guest was a better fit than leaving them store-backed symlinks
- adding a direct activation log materially improves post-boot diagnosis and avoids guessing whether activation actually completed
- the first attempt exposed a missing-userland dependency (`touch`) quickly; because the new diagnostics were explicit, the follow-up fix was immediate and local
- both validated boot paths still hold after this change:
- `freebsd-init+rc.d-shepherd`
- `shepherd-pid1`
Current assessment:
- the current Fruix guest remains intentionally minimal, but its runtime behavior is now less prototype-noisy and easier to inspect as a basic FreeBSD-like system
- this is exactly the kind of targeted hardening that makes the existing system a better launch point for native FreeBSD base-build work
Next recommended step:
1. complete Phase 12.3 by making the host-staged FreeBSD base boundary explicit in the package/model layer and docs
2. document the first intended replacement order for native world/kernel work
3. then begin Phase 13 with a clearer transitional boundary
## 2026-04-02 — Phase 12.3: made the host-staged FreeBSD base boundary explicit
Completed work:
- wrote the Phase 12.3 report:
- `docs/reports/phase12-host-staged-base-boundary-freebsd.md`
- refined `modules/fruix/packages/freebsd.scm` so the transitional host-copy base boundary is now explicit in the package/model layer
- added and exported named transitional package sets:
- `%freebsd-host-staged-all-packages`
- `%freebsd-host-staged-core-packages`
- `%freebsd-host-staged-development-profile-packages`
- `%freebsd-host-staged-system-packages`
- added and exported:
- `freebsd-host-staged-package?`
- `%freebsd-host-staged-replacement-order`
- preserved compatibility aliases so existing callers still work:
- `%freebsd-core-packages`
- `%freebsd-development-profile-packages`
- `%freebsd-system-packages`
- encoded the intended first replacement order for native base-build work directly in the package layer:
1. `freebsd-kernel`, `freebsd-bootloader`
2. `freebsd-runtime`, `freebsd-libc`, `freebsd-userland`, `freebsd-rc-scripts`
3. `freebsd-networking`, `freebsd-openssh`
4. `freebsd-kernel-headers`, `freebsd-clang-toolchain`
5. `freebsd-gmake`, `freebsd-autotools`, `freebsd-openssl`, `freebsd-zlib`, `freebsd-sh`, `freebsd-bash`
- updated `modules/fruix/system/freebsd.scm` so the generated closure metadata now carries this boundary information too:
- `metadata/store-layout.scm` now includes:
- `host-base-stores`
- `fruix-runtime-stores`
- `host-base-replacement-order`
- `init-mode`
Validation:
- confirmed package-layer behavior directly with Guile:
- `(freebsd-host-staged-package? freebsd-runtime)` => `#t`
- `%freebsd-host-staged-replacement-order` prints the expected staged replacement order
- `tests/system/run-phase7-system-closure.sh` still passes after the package-layer clarification:
- workdir: `/tmp/phase12-3-closure-1775162784`
- inspected generated closure metadata file:
- `/frx/store/25ae9bb85da60b8c77971325e0e11d5390a064132a35e1bab0866cabb802a606-fruix-system-fruix-freebsd/metadata/store-layout.scm`
- confirmed it now includes:
- `host-base-stores`
- `fruix-runtime-stores`
- `host-base-replacement-order`
Important findings:
- the current host-staged FreeBSD base model is no longer just an implicit fact of the implementation; it is now named and documented as a transitional boundary
- preserving compatibility aliases means the current working system model does not need a broad rename/refactor just to make that boundary explicit
- encoding the replacement order directly in the package/model layer gives Phase 13 a clearer starting point for native `world`/`kernel` work
Current assessment:
- Phase 12 is now complete
- the current Fruix pipeline is better documented, easier to diagnose, less noisy at runtime, and clearer about what remains transitional in the FreeBSD base layer
- this is a good stopping point before beginning native FreeBSD base-build artifacts in `/frx/store`
Next recommended step:
1. begin Phase 13.1 by modeling FreeBSD `world` and `kernel` as Fruix-managed build artifacts rather than host-copy packages
2. use `/usr/src` as the initial source of truth on the builder side
3. target the first bootable replacement for the current host-staged kernel and core runtime path
## 2026-04-02 — Phase 13.1: modeled native FreeBSD world/kernel artifacts
Completed work:
- wrote the Phase 13.1 report:
- `docs/reports/phase13-native-base-model-freebsd.md`
- added native FreeBSD base package objects in `modules/fruix/packages/freebsd.scm`:
- `freebsd-native-kernel`
- `freebsd-native-world`
- added and exported:
- `freebsd-native-build-package?`
- encoded the first native build parameters directly in those package definitions, including:
- `source-root=/usr/src`
- `target=amd64`
- `target-arch=amd64`
- `kernconf=GENERIC`
- make flags:
- `__MAKE_CONF=/dev/null`
- `SRCCONF=/dev/null`
- `SRC_ENV_CONF=/dev/null`
- `MK_DEBUG_FILES=no`
- `MK_TESTS=no`
- the first native world artifact now also carries an explicit runtime-oriented prune list:
- `usr/share/doc`
- `usr/share/examples`
- `usr/share/info`
- `usr/share/man`
- `usr/tests`
- extended `modules/fruix/system/freebsd.scm` so `materialize-freebsd-package` now understands:
- `copy-build-system`
- `freebsd-world-build-system`
- `freebsd-kernel-build-system`
- added native-build identity/materialization helpers for:
- `/usr/src` source-tree identity
- `KERNCONF` path hashing
- build-root identity
- buildworld/buildkernel stamp handling
- staged installworld/installkernel materialization
- native build metadata files in store outputs
- chose an `mtree`-based `/usr/src` identity for the first native output model using:
- `type`
- `link`
- `size`
- `mode`
- `sha256digest`
- updated closure/image metadata modeling so the system can now distinguish:
- `host_base_stores`
- `native_base_stores`
- `fruix_runtime_stores`
- updated profile/tree merging to skip private dotfile metadata from store outputs so native-build metadata does not leak into the merged runtime tree
Validation:
- confirmed the updated package/system modules still load after the native build-model additions
- confirmed the new native package objects are present and classified as expected:
- `freebsd-native-kernel` reports build-system `freebsd-kernel-build-system`
- `freebsd-native-build-package? freebsd-native-world` returns `#t`
- re-ran the existing host-copy regression check successfully:
- `tests/system/run-phase7-system-closure.sh`
- workdir: `/tmp/phase13-1-closure-1775164392`
- result: `PASS phase7-system-closure`
Important findings:
- Fruix now has a real model for FreeBSD base artifacts built from `/usr/src`; the project is no longer limited to describing the FreeBSD base only as host-copy packages
- the first native identity story is explicit:
- `/usr/src` contributes through an `mtree`-based tree digest
- `KERNCONF` contributes through its resolved path hash
- selected make/build parameters are part of the manifest too
- the repo can now describe a mixed system more honestly by separating:
- transitional host-staged base stores
- native base stores
- Fruix runtime stores
Current assessment:
- Phase 13.1 is complete
- the next step is no longer architectural guesswork; it is concrete execution of the newly added native package/materialization path
Next recommended step:
1. build the first concrete `freebsd-native-kernel` and `freebsd-native-world` outputs from `/usr/src`
2. inspect/document their staged contents in `/frx/store`
3. then wire a bootable system closure/image around those native outputs
## 2026-04-03 — Phase 13.2: first native FreeBSD world/kernel outputs built from `/usr/src`
Completed work:
- wrote the Phase 13.2 report:
- `docs/reports/phase13-native-world-kernel-build-freebsd.md`
- exercised the new native build path for real and produced the first concrete store outputs from `/usr/src`:
- native kernel output under `/frx/store`
- native world output under `/frx/store`
- added a dedicated Phase 13.2 template/harness:
- `tests/system/phase13-native-base-pid1-operating-system.scm.in`
- `tests/system/run-phase13-native-base-build.sh`
- fixed the first real native-build failure:
- `MAKEOBJDIRPREFIX` cannot be passed as a make command-line variable for this FreeBSD build path
- changed the native builder to invoke make as:
- `env MAKEOBJDIRPREFIX=... make ...`
- fixed a subtle output-identity bug in the first `/usr/src` hash implementation:
- the initial `mtree`-based source hash accidentally included unstable header comments such as date/user/machine lines
- now strips `# ...` header lines before hashing
- this stabilized the source-tree identity and stopped the native output/build-root identity from drifting on each run
- verified that native world and kernel now share the same:
- `source-tree-sha256`
- `build-root`
- the first native world split remains intentionally runtime-oriented and prunes at least:
- `usr/share/doc`
- `usr/share/examples`
- `usr/share/info`
- `usr/share/man`
- `usr/tests`
Concrete validated outputs:
- native kernel store path:
- `/frx/store/93f35ddcb9a03f63f83c9e8ae29788685d339789da664f881822b4a1914f5ff6-freebsd-native-kernel-15.0-STABLE`
- native world store path:
- `/frx/store/3f6f7f8c06ed8dad4cae21a1e8ac8ba4823bdb7cf54328c9bbcccaeb858beb77-freebsd-native-world-15.0-STABLE`
- shared native build root:
- `/var/tmp/fruix-freebsd-native-build-c59b1b8128b305d9bad9cf3d654771c941c4e8b6a2732f6bc959df96d1d32f58`
Validated native-world contents include at least:
- `/bin/sh`
- `/sbin/init`
- `/etc/rc`
- `/usr/sbin/sshd`
- `/sbin/dhclient`
- `/usr/bin/cap_mkdb`
- `/usr/sbin/pwd_mkdb`
- `/usr/share/locale/C.UTF-8/LC_CTYPE`
Validated pruned paths are absent from the world output:
- `/usr/share/man`
- `/usr/tests`
Validation:
- real native-base `fruix system build` succeeded with an operating-system using:
- `freebsd-native-kernel`
- `freebsd-native-world`
- host-staged `freebsd-bootloader`
- `shepherd-pid1`
- `tests/system/run-phase13-native-base-build.sh` passes:
- workdir: `/tmp/phase13-2-build-1775173551`
- result: `PASS phase13-native-base-build`
- the harness confirmed:
- closure rebuild path reproducibility
- native kernel/world store paths exist
- native build info files exist
- world/kernel source-tree hashes match
- world/kernel build roots match
- native build logs exist
- `native-base-stores` is present in closure metadata
- host/native store boundary now looks as expected for this mixed system:
- `host_base_store_count=1`
- `native_base_store_count=2`
Important findings:
- the native build path is now real, not just modeled
- the first mixed Phase-13 system boundary is explicit and sensible:
- host-staged bootloader only
- native kernel + native world
- Fruix runtime stores unchanged
- the `mtree` preamble bug would have made native output identity drift across runs; fixing it was essential before treating these as reproducible store artifacts
- the shared build-root result is important: the kernel/world pair now reuses the same `/usr/src` build state instead of acting like two unrelated builds
Current assessment:
- Phase 13.2 is complete
- Fruix can now build and stage native FreeBSD base artifacts from `/usr/src` in `/frx/store`
- the next step is to boot a system using those native outputs rather than stopping at build-time inspection
Next recommended step:
1. wire the image/boot path to use the native kernel/world outputs end-to-end
2. validate locally with QEMU/UEFI
3. validate on the approved XCP-ng VM and VDI path
## 2026-04-03 — Phase 13.3: booted Fruix from native FreeBSD kernel/world outputs
Completed work:
- wrote the Phase 13.3 report:
- `docs/reports/phase13-native-base-boot-freebsd.md`
- completed the first end-to-end boot path using native `/usr/src`-built FreeBSD base artifacts in `/frx/store`
- fixed the first native-image sizing problem:
- the old fixed `root-size=256m` was too small once the image carried a native world
- added explicit root filesystem sizing support to the CLI/image path:
- `scripts/fruix.scm` now accepts `--root-size SIZE`
- image metadata now records `root_size`
- `tests/system/run-phase8-system-image.sh` now accepts:
- `ROOT_SIZE`
and records:
- `root_size`
- `native_base_store_count`
- `native_base_stores`
- fixed a follow-up image metadata bug:
- `materialize-bhyve-image` now returns:
- `native-base-stores`
- this removed the `length #f` failure in the native image path
- generalized the local PID1 QEMU harness a bit further:
- `tests/system/run-phase11-shepherd-pid1-qemu.sh` now accepts `OS_TEMPLATE`
- added dedicated Phase 13.3 native-base boot wrappers:
- `tests/system/run-phase13-native-base-qemu.sh`
- `tests/system/run-phase13-native-base-xcpng.sh`
- these wrappers reuse the validated PID1 boot path but additionally require the image metadata to prove the booted system is really using the intended Phase-13 base split:
- native kernel present
- native world present
- host base reduced to bootloader only
Working native-base boot configuration:
- local QEMU:
- `ROOT_SIZE=6g`
- `DISK_CAPACITY=8g`
- real XCP-ng:
- `ROOT_SIZE=6g`
- disk capacity kept matched to the fixed 30 GiB VDI as before
Validation:
- local QEMU/UEFI/TCG boot passes through the new wrapper:
- `tests/system/run-phase13-native-base-qemu.sh`
- workdir: `/tmp/phase13-3-qemu3-1775174863`
- result: `PASS phase13-native-base-qemu`
- confirmed:
- `disk_capacity=8g`
- `root_size=6g`
- `native_base_store_count=2`
- `host_base_store_count=1`
- `shepherd_pid=1`
- `sshd_status=running`
- `native_base_boot=ok`
- real XCP-ng boot passes through the new wrapper:
- `tests/system/run-phase13-native-base-xcpng.sh`
- workdir: `/tmp/phase13-3-xcpng-1775175086`
- result: `PASS phase13-native-base-xcpng`
- confirmed:
- `vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289`
- `vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `guest_ip=192.168.213.62`
- `root_size=6g`
- `native_base_store_count=2`
- `host_base_store_count=1`
- `shepherd_pid=1`
- `sshd_status=running`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `native_base_boot=ok`
- validated native-base closure/image composition now boots with:
- native kernel store:
- `/frx/store/93f35ddcb9a03f63f83c9e8ae29788685d339789da664f881822b4a1914f5ff6-freebsd-native-kernel-15.0-STABLE`
- native world store:
- `/frx/store/3f6f7f8c06ed8dad4cae21a1e8ac8ba4823bdb7cf54328c9bbcccaeb858beb77-freebsd-native-world-15.0-STABLE`
- remaining host base store:
- `/frx/store/8ffcfe0356fea815726b610514a1280a11266851c2acb870047d559795569f0e-freebsd-bootloader-15.0-STABLE`
Important findings:
- the native world is large enough that the older 256 MiB rootfs assumption is no longer realistic; explicit image sizing is now part of the practical Phase-13 path
- after the native-base transition, the remaining transitional boundary is now much narrower and explicit:
- host-staged bootloader/boot assets
- native kernel
- native core world runtime
- the real XCP-ng upload path still works with the larger native-world image, but the dynamic VHD is naturally much bigger now (~4.42 GiB)
Current assessment:
- Phase 13 is complete
- Fruix now builds FreeBSD kernel/world artifacts from `/usr/src` into `/frx/store` and successfully boots a declarative system from those native outputs
- this is the main architectural pivot Plan 3 called for before Phase 14
Next recommended step:
1. begin Phase 14 by replacing the remaining host-copy boot assets first
2. keep shrinking the host-staged base boundary around the now-working native world/kernel path
3. revisit cleaner runtime vs. development splits after the boot asset transition
## 2026-04-03 — Phase 14.1: removed host-copied boot assets from the validated native boot path
Completed work:
- wrote the Phase 14.1 report:
- `docs/reports/phase14-native-boot-assets-freebsd.md`
- added a dedicated Phase 14.1 native-boot PID1 template:
- `tests/system/phase14-native-boot-pid1-operating-system.scm.in`
- added dedicated Phase 14.1 validation wrappers:
- `tests/system/run-phase14-native-boot-qemu.sh`
- `tests/system/run-phase14-native-boot-xcpng.sh`
- validated a cleaner native boot path by sourcing boot assets from the existing native world output instead of the host-staged `freebsd-bootloader` package:
- `#:kernel freebsd-native-kernel`
- `#:bootloader freebsd-native-world`
- `#:base-packages (list freebsd-native-world)`
- hardened the reusable local PID1 QEMU harness so it no longer boots the raw store image directly read/write from `/frx/store`:
- `tests/system/run-phase11-shepherd-pid1-qemu.sh` now copies the generated raw image to:
- `boot-disk.img`
in the workdir before launching QEMU
- this prevents repeated local boots from mutating the supposed store artifact and causing dirty-filesystem follow-up failures
Validation:
- local QEMU/UEFI/TCG native-boot wrapper passes:
- `tests/system/run-phase14-native-boot-qemu.sh`
- workdir: `/tmp/phase14-1-qemu2-1775188371`
- result: `PASS phase14-native-boot-qemu`
- confirmed:
- `native_base_store_count=2`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `native_boot_assets=freebsd-native-world`
- `native_base_boot=ok`
- real XCP-ng native-boot wrapper passes:
- `tests/system/run-phase14-native-boot-xcpng.sh`
- workdir: `/tmp/phase14-1-xcpng-1775188701`
- result: `PASS phase14-native-boot-xcpng`
- confirmed:
- `vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289`
- `vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `guest_ip=192.168.213.62`
- `native_base_store_count=2`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `native_boot_assets=freebsd-native-world`
- `native_base_boot=ok`
Current assessment:
- Phase 14.1 is complete
- the validated native boot path no longer depends on host-copied `/boot/...` material
- the current Phase-14.1 native boundary is now fully host-base-free for the validated path:
- native kernel
- native world supplying boot assets
- native world supplying runtime
Next recommended step:
1. introduce a clearer native runtime slice so runtime is no longer modeled by reusing the broader native world output for both boot and runtime
2. validate that explicit native runtime slice on QEMU and XCP-ng
3. then revisit headers/toolchain/development package boundaries
## 2026-04-03 — Phase 14.2: validated an explicit native FreeBSD runtime slice
Completed work:
- wrote the Phase 14.2 report:
- `docs/reports/phase14-native-runtime-freebsd.md`
- added a new native package in `modules/fruix/packages/freebsd.scm`:
- `freebsd-native-runtime`
- this runtime slice is built from `/usr/src` through the native world path and now prunes at least:
- `boot`
- `usr/include`
- `usr/share/doc`
- `usr/share/examples`
- `usr/share/info`
- `usr/share/man`
- `usr/share/mk`
- `usr/tests`
- added a dedicated Phase 14.2 operating-system template:
- `tests/system/phase14-native-runtime-pid1-operating-system.scm.in`
- added dedicated validation wrappers:
- `tests/system/run-phase14-native-runtime-qemu.sh`
- `tests/system/run-phase14-native-runtime-xcpng.sh`
- the validated Phase 14.2 model now uses:
- `#:kernel freebsd-native-kernel`
- `#:bootloader freebsd-native-world`
- `#:base-packages (list freebsd-native-runtime)`
- this makes the system composition more explicit:
- native world provides boot assets
- native runtime provides the guest runtime slice
- host base stores remain absent from the validated path
Important finding:
- this Phase 14.2 layout still duplicates some content because boot assets still come from the broader native world output while runtime comes from the separate native runtime slice
- that made the earlier Phase 13 image sizes too small
- the working values are now:
- local QEMU:
- `DISK_CAPACITY=12g`
- `ROOT_SIZE=10g`
- real XCP-ng:
- `ROOT_SIZE=10g`
- disk capacity still matched to the fixed 30 GiB VDI
- the new Phase 14.2 wrappers now use those larger defaults
Validation:
- local QEMU/UEFI/TCG runtime wrapper passes:
- `tests/system/run-phase14-native-runtime-qemu.sh`
- workdir: `/tmp/phase14-2-qemu2-1775189802`
- result: `PASS phase14-native-runtime-qemu`
- confirmed:
- `disk_capacity=12g`
- `root_size=10g`
- `runtime_store=/frx/store/684a82aeed2c9a353e3a09d2cbf5358274d758005e0bfa9b1025d101bc166f79-freebsd-native-runtime-15.0-STABLE`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `native_runtime_ready=ok`
- real XCP-ng runtime wrapper passes:
- `tests/system/run-phase14-native-runtime-xcpng.sh`
- workdir: `/tmp/phase14-2-xcpng-1775190184`
- result: `PASS phase14-native-runtime-xcpng`
- confirmed:
- `vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289`
- `vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `guest_ip=192.168.213.62`
- `root_size=10g`
- `runtime_store=/frx/store/684a82aeed2c9a353e3a09d2cbf5358274d758005e0bfa9b1025d101bc166f79-freebsd-native-runtime-15.0-STABLE`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `native_runtime_ready=ok`
- the wrappers also assert the runtime-split boundary directly:
- runtime store contains required boot-to-ready files such as:
- `/bin/sh`
- `/sbin/init`
- `/etc/rc`
- `/usr/sbin/sshd`
- `/sbin/dhclient`
- `/usr/bin/ssh-keygen`
- `/usr/share/locale/C.UTF-8/LC_CTYPE`
- runtime store no longer contains:
- `/boot`
- `/usr/include`
Current assessment:
- Phase 14.2 is complete
- the validated Fruix guest now reaches ready state using an explicit native runtime artifact rather than reusing the broad native world output for both boot and runtime roles
- the validated path remains host-base-free:
- native kernel
- native world as the temporary boot-source artifact
- native runtime as the guest runtime artifact
Next recommended step:
1. define cleaner runtime vs. development boundaries in code/package sets
2. introduce a narrower native boot asset package so the broader native world output is no longer needed as the temporary boot-source store
3. revalidate the full host-base-free path after that split
## 2026-04-03 — Phase 14.3: split native FreeBSD boot, runtime, and development artifacts
Completed work:
- wrote the Phase 14.3 report:
- `docs/reports/phase14-native-splits-freebsd.md`
- added new native packages in `modules/fruix/packages/freebsd.scm`:
- `freebsd-native-bootloader`
- `freebsd-native-headers`
- refined `freebsd-native-runtime` into a narrower runtime slice by pruning more obviously non-runtime content, including at least:
- `boot`
- `rescue`
- `usr/include`
- `usr/lib/debug`
- `usr/lib32`
- `usr/obj`
- `usr/src`
- `usr/share/doc`
- `usr/share/examples`
- `usr/share/info`
- `usr/share/man`
- `usr/share/mk`
- `usr/tests`
- extended the native world-derived build path in `modules/fruix/system/freebsd.scm` with support for:
- `keep-paths`
- native build manifests/output metadata now record both:
- `keep-paths`
- `prune-paths`
- added explicit native package-set boundaries:
- `%freebsd-native-system-packages`
- `%freebsd-native-development-profile-packages`
- the final validated Phase 14 system template now uses:
- `#:kernel freebsd-native-kernel`
- `#:bootloader freebsd-native-bootloader`
- `#:base-packages %freebsd-native-system-packages`
- this removes the need for the broad `freebsd-native-world` artifact from the final validated system closure
New validation files:
- `tests/system/phase14-native-split-pid1-operating-system.scm.in`
- `tests/system/run-phase14-native-split-qemu.sh`
- `tests/system/run-phase14-native-split-xcpng.sh`
- `tests/system/run-phase14-native-development-split.sh`
Validation:
- development/runtime split harness passes:
- `tests/system/run-phase14-native-development-split.sh`
- workdir: `/tmp/phase14-3-dev5-1775191195`
- result: `PASS phase14-native-development-split`
- confirmed:
- `bootloader_store=/frx/store/71aa3ba5dd9a02f7d2710bfc3624cbf5e3cd18f1fbff0744c82df36901b10ec0-freebsd-native-bootloader-15.0-STABLE`
- `headers_store=/frx/store/aab09122d37962e6d479c17172ce4b8ea85e5ff33c98aa76424ada2fa1a82617-freebsd-native-headers-15.0-STABLE`
- `native_system_packages=freebsd-native-runtime`
- `native_development_packages=freebsd-native-runtime,freebsd-native-headers,freebsd-clang-toolchain,freebsd-gmake,freebsd-autotools,freebsd-openssl,freebsd-zlib,freebsd-sh,freebsd-bash`
- `runtime_vs_development_split=ok`
- the harness also confirmed:
- bootloader slice contains the expected boot assets only
- headers slice contains headers/mk files
- headers slice does not contain `/boot` or runtime binaries
- final split-system local QEMU validation passes:
- `tests/system/run-phase14-native-split-qemu.sh`
- workdir: `/tmp/phase14-3-qemu-1775191337`
- result: `PASS phase14-native-split-qemu`
- confirmed:
- `disk_capacity=8g`
- `root_size=6g`
- `bootloader_store=/frx/store/71aa3ba5dd9a02f7d2710bfc3624cbf5e3cd18f1fbff0744c82df36901b10ec0-freebsd-native-bootloader-15.0-STABLE`
- `runtime_store=/frx/store/1b4b8774d0df36df2635fe1c35367a2c5fa7790e303f0aaa26eabfe3cce667f2-freebsd-native-runtime-15.0-STABLE`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `native_split_boot=ok`
- final split-system real XCP-ng validation passes:
- `tests/system/run-phase14-native-split-xcpng.sh`
- workdir: `/tmp/phase14-3-xcpng-1775191743`
- result: `PASS phase14-native-split-xcpng`
- confirmed:
- `vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289`
- `vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `guest_ip=192.168.213.62`
- `root_size=6g`
- `bootloader_store=/frx/store/71aa3ba5dd9a02f7d2710bfc3624cbf5e3cd18f1fbff0744c82df36901b10ec0-freebsd-native-bootloader-15.0-STABLE`
- `runtime_store=/frx/store/1b4b8774d0df36df2635fe1c35367a2c5fa7790e303f0aaa26eabfe3cce667f2-freebsd-native-runtime-15.0-STABLE`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `shepherd_pid=1`
- `sshd_status=running`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
- `native_split_boot=ok`
Important findings:
- after replacing the temporary broad-world boot source with the narrower native bootloader slice and tightening the runtime slice, the validated image became much smaller again
- the real XCP-ng dynamic VHD upload for the final split path was:
- `1560725504` bytes (~1.45 GiB)
- that is far better than the temporary broad-world + runtime duplication in Phase 14.2
- the final validated Phase 14 closure no longer needs the broad `freebsd-native-world` artifact in its native base store set
Current assessment:
- Phase 14 is complete
- Fruix now has a validated, host-base-free FreeBSD system path composed from native artifacts in `/frx/store`:
- `freebsd-native-kernel`
- `freebsd-native-bootloader`
- `freebsd-native-runtime`
- the runtime vs. development boundary is also clearer now through:
- `freebsd-native-headers`
- `%freebsd-native-development-profile-packages`
- this completes the Plan-3 Phase-14 goal of incrementally replacing the host-copy FreeBSD base layer for the validated boot/runtime path
Next recommended step:
1. begin Phase 15 by making the FreeBSD base version a declarative Fruix input
2. demonstrate side-by-side native base versions in `/frx/store`
3. validate rebuild/redeploy/rollback across those base versions
## 2026-04-03 — Phase 15.1: made the FreeBSD base a declarative Fruix input
Completed work:
- wrote the Phase 15.1 report:
- `docs/reports/phase15-declarative-base-freebsd.md`
- added a new declarative base record in `modules/fruix/packages/freebsd.scm`:
- `freebsd-base`
- `freebsd-base?`
- `%default-freebsd-base`
- the declarative base now records at least:
- `name`
- `version-label`
- `release`
- `branch`
- `source-root`
- `target`
- `target-arch`
- `kernconf`
- `make-flags`
- native FreeBSD package constructors are now parameterized by a declared base:
- `freebsd-native-kernel-for`
- `freebsd-native-world-for`
- `freebsd-native-runtime-for`
- `freebsd-native-bootloader-for`
- `freebsd-native-headers-for`
- `freebsd-native-system-packages-for`
- `freebsd-native-development-profile-packages-for`
- existing exported native package variables remain available as the `%default-freebsd-base` instances:
- `freebsd-native-kernel`
- `freebsd-native-world`
- `freebsd-native-runtime`
- `freebsd-native-bootloader`
- `freebsd-native-headers`
- added a new operating-system field in `modules/fruix/system/freebsd.scm`:
- `#:freebsd-base`
- exported accessor: `operating-system-freebsd-base`
- the declared base is now recorded in system/image metadata through:
- `operating-system-closure-spec`
- `operating-system-image-spec`
- `metadata/freebsd-base.scm`
- `metadata/store-layout.scm`
- native build manifests and `.freebsd-native-build-info.scm` now record:
- `declared-base`
- `scripts/fruix.scm` now emits declared base metadata for `build` and `image`:
- `freebsd_base_name`
- `freebsd_base_version_label`
- `freebsd_base_release`
- `freebsd_base_branch`
- `freebsd_base_source_root`
- `freebsd_base_target`
- `freebsd_base_target_arch`
- `freebsd_base_kernconf`
- `freebsd_base_file`
New validation files:
- `tests/system/phase15-declarative-base-pid1-operating-system.scm.in`
- `tests/system/run-phase15-declarative-base-build.sh`
Validation:
- declarative-base build harness passes:
- `tests/system/run-phase15-declarative-base-build.sh`
- workdir: `/tmp/phase15-1-build-1775202535`
- result: `PASS phase15-declarative-base-build`
- confirmed:
- `kernel_store=/frx/store/8fcef04c7e507e86ea5e92f251fe3c6ac1aa3bcf4809fa77ddd8b92854bfcde0-freebsd-native-kernel-15.0-STABLE-declarative`
- `bootloader_store=/frx/store/7a0ba431e487dc35a8f6318108da16a37c8426c43e77e7a7f91404ba1d980eef-freebsd-native-bootloader-15.0-STABLE-declarative`
- `runtime_store=/frx/store/17c24ad20ddcb136c39352b68e758deae0b480258ba0128a5546f696a7eba0a6-freebsd-native-runtime-15.0-STABLE-declarative`
- `native_base_store_count=3`
- `host_base_store_count=0`
- `freebsd_base_name=stable-default`
- `freebsd_base_version_label=15.0-STABLE-declarative`
- `freebsd_base_release=15.0-STABLE`
- `freebsd_base_branch=stable/15`
- `freebsd_base_source_root=/usr/src`
- `freebsd_base_kernconf=GENERIC`
- `declarative_base_input=ok`
- the harness also confirmed:
- `metadata/freebsd-base.scm` exists
- `parameters.scm` records the declared base
- `metadata/store-layout.scm` records the declared base
- native build info files record the declared base version/branch
Current assessment:
- Phase 15.1 is complete
- Fruix now models the FreeBSD base as an explicit declarative system input instead of leaving it implicit in the builder host alone
- the next step is to use that new declaration to prove side-by-side base versions and rollback-friendly rebuild/redeploy behavior
## 2026-04-03 — Phase 15.2: validated side-by-side base versions and rollback-friendly redeploy
Completed work:
- wrote the Phase 15.2 report:
- `docs/reports/phase15-base-upgrades-freebsd.md`
- added validation harnesses:
- `tests/system/run-phase15-base-coexistence.sh`
- `tests/system/run-phase15-base-rollback-qemu.sh`
- `tests/system/run-phase15-base-rollback-xcpng.sh`
- used two explicit declarative base identities against the current validated native Phase 14 package split:
- current base:
- `name=stable-default`
- `version-label=15.0-STABLE`
- `release=15.0-STABLE`
- `branch=stable/15`
- candidate base:
- `name=stable-canary`
- `version-label=15.0-STABLE-p1`
- `release=15.0-STABLE`
- `branch=stable/15`
- both declarations still use the same local `/usr/src`, but now produce distinct declared base/store/deployment identities
Validation:
- side-by-side base-coexistence harness passes:
- `tests/system/run-phase15-base-coexistence.sh`
- workdir: `/tmp/phase15-2-coexist-1775202833`
- result: `PASS phase15-base-coexistence`
- confirmed:
- `current_closure=/frx/store/9f57ecc6481e271811ceb53ac21a3b2aef4ef329f82b7d4788622315db1f0e43-fruix-system-fruix-freebsd`
- `candidate_closure=/frx/store/dc40b1b7a76084e140d0457f3b7f6c5d4acc185f0d6cee0b161c9775d5fb3bec-fruix-system-fruix-freebsd`
- `current_base_version_label=15.0-STABLE`
- `candidate_base_version_label=15.0-STABLE-p1`
- `side_by_side_base_versions=ok`
- `rollback_rebuild_path=ok`
- this also confirmed that:
- both closures exist side by side in `/frx/store`
- rebuilding the current declaration returns the exact original current closure path
- current native base stores remain separate from candidate native base stores
- local QEMU rollback harness passes:
- `tests/system/run-phase15-base-rollback-qemu.sh`
- workdir: `/tmp/phase15-2-qemu2-1775204321`
- result: `PASS phase15-base-rollback-qemu`
- validation sequence:
1. boot current base
2. boot candidate base
3. boot current base again
- confirmed:
- `current_first_closure=/frx/store/9f57ecc6481e271811ceb53ac21a3b2aef4ef329f82b7d4788622315db1f0e43-fruix-system-fruix-freebsd`
- `candidate_closure=/frx/store/dc40b1b7a76084e140d0457f3b7f6c5d4acc185f0d6cee0b161c9775d5fb3bec-fruix-system-fruix-freebsd`
- `rollback_closure=/frx/store/9f57ecc6481e271811ceb53ac21a3b2aef4ef329f82b7d4788622315db1f0e43-fruix-system-fruix-freebsd`
- `current_base_version_label=15.0-STABLE`
- `candidate_base_version_label=15.0-STABLE-p1`
- `rollback_base_version_label=15.0-STABLE`
- `base_rollforward_and_rollback=ok`
- real XCP-ng rollback harness passes:
- `tests/system/run-phase15-base-rollback-xcpng.sh`
- workdir: `/tmp/phase15-2-xcpng-1775204839`
- result: `PASS phase15-base-rollback-xcpng`
- validation sequence:
1. boot candidate base on the approved VM/VDI
2. boot current base again on the same approved VM/VDI
- confirmed:
- `candidate_closure=/frx/store/dc40b1b7a76084e140d0457f3b7f6c5d4acc185f0d6cee0b161c9775d5fb3bec-fruix-system-fruix-freebsd`
- `rollback_closure=/frx/store/9f57ecc6481e271811ceb53ac21a3b2aef4ef329f82b7d4788622315db1f0e43-fruix-system-fruix-freebsd`
- `candidate_base_version_label=15.0-STABLE-p1`
- `rollback_base_version_label=15.0-STABLE`
- `vm_id=90490f2e-e8fc-4b7a-388e-5c26f0157289`
- `vdi_id=0f1f90d3-48ca-4fa2-91d8-fc6339b95743`
- `base_rollforward_and_rollback=ok`
- guest invariants stayed intact for both boots:
- `shepherd_pid=1`
- `sshd_status=running`
- `compat_prefix_shims=absent`
- `guile_module_smoke=ok`
Current assessment:
- Phase 15.2 is complete
- Fruix now supports a real upgrade-style FreeBSD base workflow at the store/deployment level:
- explicit current vs. candidate base declarations
- side-by-side native base outputs in `/frx/store`
- rollback to the earlier closure without mutating it in place
- the remaining Phase 15 work is to document the evidence-based decision on whether self-hosted base builds should be the next step, or whether host-built native base artifacts should remain the near-term path while reproducibility/source acquisition improve
## 2026-04-03 — Phase 15.3: decided not to pursue self-hosted base builds yet
Completed work:
- wrote the Phase 15.3 report:
- `docs/reports/phase15-self-hosting-decision-freebsd.md`
- updated the high-level summary:
- `docs/PROG_SUMMARY.md`
- recorded an evidence-based decision about the next architecture step after Phases 1315
Decision:
- do **not** pursue self-hosted FreeBSD base builds as the next immediate milestone
- keep the near-term path on:
1. host-built native FreeBSD base artifacts from `/usr/src`
2. storage in `/frx/store`
3. stronger declarative source-tree/version selection and provenance
4. tighter reproducibility around source inputs and build parameters
- only revisit guest self-hosting after those pieces are stronger
Evidence used for the decision:
- Fruix already builds native FreeBSD base artifacts from `/usr/src` into `/frx/store`
- Fruix already validates a host-base-free boot/runtime path composed from:
- `freebsd-native-kernel`
- `freebsd-native-bootloader`
- `freebsd-native-runtime`
- that path already boots on:
- local QEMU/UEFI/TCG
- the approved real XCP-ng VM/VDI path
- the FreeBSD base is now an explicit declarative input through `freebsd-base`
- Fruix now supports side-by-side declared base versions and rollback-friendly redeploy
- the most important remaining reproducibility gap is now source-tree selection/acquisition, not host-copy boot/runtime assembly
- environment constraints still argue for caution before a self-hosting pivot:
- local bhyve remains blocked under Xen due to missing nested VT-x exposure
- real validation still reuses a single approved XCP-ng VM/VDI pair
- XCP-ng storage permissions still prevent creating fresh VDIs on demand
Current assessment:
- Phase 15.3 is complete
- Phase 15 is fully complete
- Fruix now has:
- a host-base-free native FreeBSD boot/runtime path in `/frx/store`
- an explicit declarative FreeBSD base model
- side-by-side base-version coexistence in `/frx/store`
- rollback-friendly redeploy across declared base versions
- a documented decision to continue with host-built native base artifacts for now rather than jumping immediately to guest self-hosting
Next recommended step:
1. focus the next phase on making FreeBSD source-tree selection/acquisition more declarative and reproducible
2. keep improving provenance and source-input identity around the now-working native base path
3. revisit self-hosted base builds only after the source/reproducibility boundary is substantially stronger