Files
fruix/docs/reports/phase12-runtime-diagnostics-freebsd.md

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_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:

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

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