6.0 KiB
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_mkdbpwd_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:startfruix-activate:cap_mkdb=okfruix-activate:pwd_mkdb=okfruix-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.confas a regular file/etc/master.passwdas a regular file
VM validation
These harnesses now check for the new runtime behavior:
tests/system/run-phase9-xcpng-boot.shtests/system/run-phase11-shepherd-pid1-xcpng.shtests/system/run-phase11-shepherd-pid1-qemu.sh
New checks include:
login_conf_kind=regularlogin_conf_db=presentpwd_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:
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:
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:
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:
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