Files
fruix/docs/reports/phase18-installer-iso-freebsd.md

7.0 KiB

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:

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:

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