# Phase 10.1: Add a real `fruix system` command for FreeBSD system artifacts Date: 2026-04-02 ## Goal Start Optional Phase 10 by replacing one of the remaining prototype-only workflows with a real user-facing Fruix command. The concrete target for this subphase was: - expose the declarative FreeBSD operating-system machinery through a true `fruix` CLI entry point, - stop relying solely on ad hoc phase-specific harness scripts for closure/image materialization, - and validate that the new command can build the existing declarative system outputs. ## Result A minimal user-facing `fruix` command now exists in this repository. New entry points: - `bin/fruix` - `scripts/fruix.scm` Supported commands in this first cut: - `fruix system build OS-FILE` - `fruix system image OS-FILE` - `fruix system rootfs OS-FILE ROOTFS-DIR` Supported options: - `--system NAME` - `--store DIR` - `--disk-capacity SIZE` - `--rootfs DIR` - `--help` The command loads a declarative operating-system definition from a Scheme file, resolves the selected operating-system binding, and then calls the existing Fruix FreeBSD system materializers directly: - `materialize-operating-system` - `materialize-rootfs` - `materialize-bhyve-image` The command prints machine-readable `key=value` metadata for the produced artifacts. ## Why this matters Up through Phase 9, the project had already achieved a real booted Fruix guest, but the operator-facing path for producing those artifacts still lived mostly in phase-specific test harnesses such as: - `tests/system/run-phase7-system-closure.sh` - `tests/system/run-phase8-system-image.sh` - `tests/system/run-phase9-xcpng-boot.sh` Those were valuable validation tools, but they were not yet a real Fruix interface. This subphase moves the project one step closer to a genuine OS workflow by making system artifact generation available through a Fruix CLI surface. ## Implementation details ### `bin/fruix` Added a shell wrapper that: - locates the repository root, - locates the Guile and Guile-extra validation prefixes already used elsewhere in the project, - ensures the required local Guile/Fibers/Shepherd pieces exist, - prepares `GUILE_LOAD_PATH` for: - `modules/` - `~/repos/guix` - and runs the Guile entry point. This keeps the new command aligned with the currently validated local FreeBSD Guile toolchain rather than inventing a separate runtime path. ### `scripts/fruix.scm` Added the actual CLI implementation. It currently: - parses `fruix system ...` actions, - supports `--help`, - loads an operating-system definition file, - resolves the selected OS variable via `--system NAME` or a small fallback list of conventional names, - validates the operating-system object, - dispatches to the existing FreeBSD system materializers, - and emits stable `key=value` metadata. ### `tests/system/run-phase10-fruix-system-command.sh` Added a dedicated validation harness for the new command. The test verifies that: 1. `fruix system build` materializes a closure under `/frx/store` 2. the returned closure contains the generated activation path 3. `fruix system image` materializes a disk image under `/frx/store` 4. the command emits the expected metadata fields for both operations ## Validation Successful validation run: - `PASS phase10-fruix-system-command` - workdir: `/tmp/phase10-fruix-cmd-1775117490` The new command successfully produced both: - a Fruix system closure path under `/frx/store/*-fruix-system-fruix-freebsd` - a Fruix image path under `/frx/store/*-fruix-bhyve-image-fruix-freebsd/disk.img` Example usage: ```sh sudo env HOME="$HOME" ./bin/fruix system build \ tests/system/phase7-minimal-operating-system.scm \ --system phase7-operating-system ``` ```sh sudo env HOME="$HOME" ./bin/fruix system image \ tests/system/phase7-minimal-operating-system.scm \ --system phase7-operating-system \ --disk-capacity 5g ``` ## Current limitations This is intentionally a first Phase 10 step, not the final Fruix command surface. Notable current limitations: - system/image materialization still expects the currently validated local Guile/Shepherd build prefixes - writing into `/frx/store` and building disk images still typically requires `sudo` - the command currently targets only the FreeBSD system prototype path already implemented in `modules/fruix/system/freebsd.scm` - it does not yet integrate with the larger upstream Guix command framework; it is a Fruix-native CLI wrapper in this repo ## Assessment This subphase successfully replaces an important transitional layer with a more OS-like Fruix interface. The project can now say not only that it can declaratively define and boot a Fruix FreeBSD system, but also that it has a direct user-facing command for materializing the resulting system artifacts. ## Recommended next step Continue Phase 10 by reducing another transitional seam behind `fruix system`, most likely one of: 1. add a `fruix system vm` / deploy-oriented flow on top of the now-working image path 2. replace the current compatibility symlink handling for locally built Guile/Shepherd prefixes with a more native store-path-aware runtime arrangement 3. factor the phase-specific system test harnesses to call `bin/fruix` directly where practical, so the command becomes the canonical operator path rather than just an additional wrapper