# Phase 12.2: tightened guest runtime diagnostics and reduced early-boot rough edges Date: 2026-04-02 ## Goal The current Fruix FreeBSD guest already booted, reached the network, and ran Shepherd. The next hardening step was to make the guest easier to diagnose and to remove a few distracting runtime rough edges that were still coming from the prototype-style `/etc` handling. The main targets were: - reduce early-boot/login-class noise around `/etc/login.conf` - ensure the FreeBSD password/login databases are created in writable guest state, not implicitly in store-backed paths - add a clear activation log inside the guest - teach the validation harnesses to assert the new behavior directly ## Root cause addressed The old rootfs layout symlinked several generated `/etc` files directly to `/run/current-system/etc/...`, including: - `/etc/login.conf` - `/etc/master.passwd` - `/etc/passwd` - `/etc/group` That worked for static lookup, but it was a poor fit for FreeBSD tools such as: - `cap_mkdb` - `pwd_mkdb` - early login-class lookups because those tools expect to work with regular files and adjacent generated database files such as: - `/etc/login.conf.db` - `/etc/pwd.db` - `/etc/spwd.db` With symlink-backed inputs, the system could still limp along, but it produced avoidable warnings and less clear runtime behavior. ## Implementation ### 1. Selected `/etc` files now become regular files in the guest rootfs `modules/fruix/system/freebsd.scm` now materializes these files as regular files in the rootfs instead of symlinks: - `/etc/passwd` - `/etc/master.passwd` - `/etc/group` - `/etc/login.conf` Other generated configuration files that do not need this treatment remain symlinked to `/run/current-system/...`. This gives FreeBSD's database tools writable, regular inputs in the guest filesystem while keeping the current system closure as the source of truth. ### 2. Activation now refreshes those files from `/run/current-system/etc` The generated activation script now refreshes the regular guest copies from the currently selected system closure before rebuilding databases. That means rebuild/redeploy still works coherently even though these specific files are no longer left as symlinks in the booted guest. ### 3. Activation now records a guest-visible log Activation now writes: - `/var/log/fruix-activate.log` with explicit markers such as: - `fruix-activate:start` - `fruix-activate:cap_mkdb=ok` - `fruix-activate:pwd_mkdb=ok` - `fruix-activate:done` - exit status marker from the shell trap This gives a direct guest-side indicator of whether activation actually completed. ### 4. Closure permissions were tightened slightly The generated closure now explicitly sets: - `etc/master.passwd` => `0600` before the rootfs/image path copies it into the guest. ### 5. Validation harnesses were upgraded #### Local image validation `tests/system/run-phase8-system-image.sh` now asserts that the mounted image contains: - `/etc/login.conf` as a regular file - `/etc/master.passwd` as a regular file #### VM validation These harnesses now check for the new runtime behavior: - `tests/system/run-phase9-xcpng-boot.sh` - `tests/system/run-phase11-shepherd-pid1-xcpng.sh` - `tests/system/run-phase11-shepherd-pid1-qemu.sh` New checks include: - `login_conf_kind=regular` - `login_conf_db=present` - `pwd_dbs=present` - activation log contains `fruix-activate:done` They also capture the activation log into metadata. ### 6. Small follow-up fix The first activation-log implementation briefly used `touch`, which is not staged in the minimal guest userland. This was corrected by switching to shell redirection: - `: >> "$logfile"` so the activation path no longer depends on an extra utility for log-file creation. ## Validation ### Local image structure Passing run: - `PASS phase8-system-image` - workdir: `/tmp/phase12-2-image-1775159011` Key metadata: ```text login_conf_kind=regular master_passwd_kind=regular ``` ### Local QEMU Shepherd PID 1 Passing run: - `PASS phase11-shepherd-pid1-qemu` - workdir: `/tmp/phase12-2b-qemu-1775161367` Key metadata: ```text activate_log=fruix-activate:start fruix-activate:cap_mkdb=ok fruix-activate:pwd_mkdb=ok fruix-activate:done fruix-activate:exit status=0 login_conf_kind=regular login_conf_db=present pwd_dbs=present shepherd_pid=1 sshd_status=running ``` ### Real VM, `freebsd-init+rc.d-shepherd` Passing run: - `PASS phase9-xcpng-boot` - workdir: `/tmp/phase12-2b-phase9-1775161731` Key metadata: ```text activate_log=fruix-activate:start fruix-activate:cap_mkdb=ok fruix-activate:pwd_mkdb=ok fruix-activate:done fruix-activate:exit status=0 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 ``` ### Real VM, `shepherd-pid1` Passing run: - `PASS phase11-shepherd-pid1-xcpng` - workdir: `/tmp/phase12-2b-phase11-1775162210` Key metadata: ```text activate_log=fruix-activate:start fruix-activate:cap_mkdb=ok fruix-activate:pwd_mkdb=ok fruix-activate:done fruix-activate:exit status=0 login_conf_kind=regular login_conf_db=present pwd_dbs=present shepherd_pid=1 compat_prefix_shims=absent guile_module_smoke=ok sshd_status=running ``` ## Assessment This was a small but high-value hardening step. The guest now behaves more like a real FreeBSD system in one of the places where a store-backed prototype can otherwise feel awkward: password/login database management and early `/etc` expectations. The important result is not cosmetic; it is operational: - activation success is now visible inside the guest - the login/password database inputs live as regular guest files where FreeBSD expects them - both validated boot modes still work locally and on the real XCP-ng VM ## Next recommended step Proceed to Phase 12.3: - make the host-staged FreeBSD base boundary explicit in the code/documentation model - group the transitional host-copy base packages more clearly - document the first intended replacement order for native world/kernel artifacts in `/frx/store`