# Phase 18.3: bootable Fruix installer ISO on FreeBSD Date: 2026-04-04 ## Goal Phase 18.3 extends the Phase 18.2 installer-environment work from a disk-image-style installer into a UEFI-bootable ISO artifact. The intended first ISO is deliberately narrow: - UEFI only - serial-console-friendly - non-interactive install flow reused from Phase 18.1/18.2 - target disk installation still performed by the same Fruix-managed in-guest installer logic ## Implementation ### New API Added in `modules/fruix/system/freebsd.scm`: - `operating-system-installer-iso-spec` - `materialize-installer-iso` The system module split done immediately before this phase was also exercised during this work. ### New CLI action Added in `scripts/fruix.scm`: - `fruix system installer-iso` This action emits metadata for: - ISO store path - ISO image path - EFI boot image path - installer root image path - installer and target closure paths - installer state/log paths - declared/materialized FreeBSD source metadata - store closure counts ### ISO boot model The ISO does not try to run the Fruix installer directly from a read-only cd9660 root. Instead it uses a small UEFI El Torito boot image plus an in-memory installer root image: 1. a small FAT EFI boot image contains `EFI/BOOT/BOOTX64.EFI` 2. the ISO root contains real boot assets under `/boot` 3. the ISO root also contains `/boot/root.img` 4. `loader.conf` on the ISO is augmented with: - `mdroot_load="YES"` - `mdroot_type="mfs_root"` - `mdroot_name="/boot/root.img"` - `vfs.root.mountfrom="ufs:/dev/md0"` - `vfs.root.mountfrom.options="rw"` A practical loader detail surfaced during validation: - setting `rootdev` or `currdev` to `md0:` in the ISO loader path is wrong for this loader configuration and caused an early EFI-loader crash before kernel handoff - the reliable ISO path is to let loader keep its current device on the CD media, preload `/boot/root.img`, and pass only `vfs.root.mountfrom=ufs:/dev/md0` This preserves the existing Fruix installer environment semantics while avoiding the need to make the whole installer operate directly from a read-only ISO root. ### Installer root image contents `materialize-installer-iso` stages the same installer payload model already validated in Phase 18.2: - installer closure - target closure - target runtime store closure needed for installation/boot - staged target rootfs under `/var/lib/fruix/installer/target-rootfs` - installer plan and state files under `/var/lib/fruix/installer` - installer helper scripts: - `/usr/local/libexec/fruix-installer-run` - `/usr/local/etc/rc.d/fruix-installer` The ISO root image is then built as a UFS image and embedded as `/boot/root.img`. ### Split-regression fixes found during this work While exercising the refactored split modules, two issues surfaced and were fixed: 1. `string-hash` name-clash warnings - the old helper name collided with Guile/SRFI bindings - it was renamed to `sha256-string` 2. missing `prefix-materializer-version` - this constant was accidentally omitted when `modules/fruix/system/freebsd.scm` was split - the missing definition was restored in `modules/fruix/system/freebsd/build.scm` ## Validation ### Completed smoke validation A host-side smoke build was completed successfully for the new ISO builder using a host-staged operating-system definition: - command pattern: - `fruix system installer-iso ...` - result: - successful ISO materialization in a temporary store - artifact checks performed: - `etdump` reports an EFI El Torito boot entry - the ISO contains: - `boot/kernel/kernel` - `boot/kernel/linker.hints` - `boot/loader.conf` - `boot/loader.efi` - `boot/root.img` - `boot/loader.conf` inside the ISO contains the expected `mdroot_*` and `vfs.root.mountfrom` entries Example smoke-build metadata: ```text action=installer-iso iso_volume_label=FRUIX_INSTALLER iso_store_path=/tmp/...-fruix-installer-iso-fruix-freebsd-installer iso_image=/tmp/...-fruix-installer-iso-fruix-freebsd-installer/installer.iso boot_efi_image=/tmp/...-fruix-installer-iso-fruix-freebsd-installer/efiboot.img root_image=/tmp/...-fruix-installer-iso-fruix-freebsd-installer/root.img installer_closure_path=/tmp/...-fruix-system-fruix-freebsd-installer target_closure_path=/tmp/...-fruix-system-fruix-freebsd ``` ### End-to-end harness validation Added: - `tests/system/run-phase18-installer-iso.sh` This harness validates the full Phase 18.3 flow: 1. build installer ISO 2. boot it under QEMU/UEFI/TCG 3. install onto a target disk from inside the booted ISO environment 4. boot the installed target Passing validation: - `PASS phase18-installer-iso` Validated result summary: ```text installer_iso_store_path=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer installer_iso_image=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer/installer.iso installer_boot_efi_image=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer/efiboot.img installer_root_image=/frx/store/...-fruix-installer-iso-fruix-freebsd-installer/root.img install_target_device=/dev/vtbd0 freebsd_source_kind=git freebsd_source_ref=stable/15 freebsd_source_commit=332708a606f6bf0841c1d4a74c0d067f5640fe89 materialized_source_store=/frx/store/459499e0eb29f4c73ad455060dd2502d21fb56f205c0a676831cf723b3a0c378-freebsd-source-stable15-installer-iso-target-source installer_state=done installer_sshd_status=running target_esp_fstype=msdosfs target_root_fstype=ufs target_shepherd_status=running target_sshd_status=running installer_iso_boot=ok installer_iso_install=ok installed_target_boot=ok ``` Notable ISO-specific validation detail: - unlike the disk-image-style installer environment from Phase 18.2, the ISO boots from `cd0`, so the target virtio disk appears as: - `/dev/vtbd0` - the earlier installer-environment default: - `/dev/vtbd1` remains correct for the disk-image installer, but not for the ISO path The harness verified all of the following: 1. `fruix system installer-iso` produces a bootable ISO artifact in `/frx/store` 2. the ISO boots successfully under QEMU/UEFI/TCG 3. the booted installer ISO environment becomes reachable over SSH 4. `/run/current-system` inside the installer ISO points at the installer closure 5. the installer rc.d job reaches: - `state=done` 6. the installer log records: - `fruix-installer:done` 7. the installed target disk contains: - GPT partitioning - EFI filesystem: `msdosfs` - root filesystem: `ufs` - `EFI/BOOT/BOOTX64.EFI` - `/var/lib/fruix/install.scm` 8. the installed target then boots successfully as its own Fruix system under QEMU/UEFI/TCG 9. after target boot: - `/run/current-system` points at the target closure - shepherd is running - `sshd` is running - activation completed successfully ## Result Phase 18.3 is complete. Fruix now has a validated bootable UEFI installer ISO on FreeBSD that can: - boot into a Fruix-managed installer environment from ISO media - perform the non-interactive installation flow onto a target disk - and boot the installed target successfully