diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 4083d7e..9e59a56 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -38,44 +38,42 @@ The validated Phase 18 installation work currently uses: ## Latest completed achievement -### 2026-04-04 — Phase 18.3 completed +### 2026-04-04 — Phase 19.1 completed -Fruix now builds a bootable installer ISO, boots it, installs from it, and boots the installed target successfully. +Fruix now has a documented canonical deployment workflow for build, image generation, installation, roll-forward, and rollback. Highlights: -- added in `modules/fruix/system/freebsd.scm`: - - `operating-system-installer-iso-spec` - - `materialize-installer-iso` -- added CLI support in `scripts/fruix.scm`: +- added canonical operator workflow documentation: + - `docs/system-deployment-workflow.md` +- added Phase 19.1 report: + - `docs/reports/phase19-deployment-workflow-freebsd.md` +- the documented command surface now explicitly centers on: + - `fruix system build` + - `fruix system rootfs` + - `fruix system image` + - `fruix system install` + - `fruix system installer` - `fruix system installer-iso` -- the installer ISO now carries: - - a UEFI El Torito boot image - - `/boot/root.img` as the installer mdroot payload - - the installer closure - - the selected target closure - - the target runtime store closure needed for installation - - in-guest installer state/log/scripts -- validated workflows: - - local QEMU/UEFI/TCG boot, install, and installed-target reboot - - real XCP-ng VM boot, install, and installed-target reboot -- a platform-specific installer detail is now recorded in-tree: - - QEMU ISO path installs onto `/dev/vtbd0` - - XCP-ng ISO path installs onto `/dev/ada0` +- the deployment story now explicitly records: + - declaration-driven roll-forward + - rollback by rebuilding/redeploying an earlier declaration + - current deployment identity via closure path and emitted provenance metadata + - current environment-specific installer target-device conventions -Validation: +Validation basis referenced by the workflow documentation: +- `PASS phase15-base-rollback-qemu` +- `PASS phase15-base-rollback-xcpng` +- `PASS phase18-system-install` +- `PASS phase18-installer-environment` - `PASS phase18-installer-iso` - `PASS phase18-installer-iso-xcpng` -Report: +Reports: -- `docs/reports/phase18-installer-iso-freebsd.md` - -Commits: - -- `1970c5c` — `system: add UEFI installer ISO builder` -- `604ad82` — `system: validate UEFI installer ISO boot path` +- `docs/system-deployment-workflow.md` +- `docs/reports/phase19-deployment-workflow-freebsd.md` ## Recent major milestones @@ -101,6 +99,6 @@ Commits: Per `docs/PLAN_4.md`, the next planned step is: -- **Phase 19.1** — define and document the canonical Fruix deployment workflow for rebuild, image generation, installation, and rollback +- **Phase 19.2** — make the installed-system generation and rollback-root model more explicit -Phase 18.3 is now complete: Fruix can build, boot, install from, and validate a bootable UEFI installer ISO on FreeBSD. +Phase 19.1 is now complete: Fruix documents a coherent operator-facing deployment workflow for build, installation, roll-forward, and rollback. diff --git a/docs/reports/phase19-deployment-workflow-freebsd.md b/docs/reports/phase19-deployment-workflow-freebsd.md new file mode 100644 index 0000000..e84bfc9 --- /dev/null +++ b/docs/reports/phase19-deployment-workflow-freebsd.md @@ -0,0 +1,143 @@ +# Phase 19.1: canonical Fruix deployment workflow on FreeBSD + +Date: 2026-04-04 + +## Goal + +Phase 19.1 is about turning Fruix's already-validated closure/image/install behavior into a clear operator-facing deployment story. + +The verification target here is documentation clarity rather than a new low-level boot primitive. + +The repo needed a single coherent explanation of how Fruix expects operators to: + +- build a system closure +- materialize a rootfs or image +- install directly to an image or block device +- use the installer image and installer ISO paths +- roll forward to a candidate declaration +- roll back to an earlier declaration + +## Result + +Phase 19.1 is complete. + +The repository now documents a first-class Fruix deployment workflow in: + +- `docs/system-deployment-workflow.md` + +That document defines the current canonical command surface and explains how the already-existing validated paths fit together operationally. + +## What was documented + +### Canonical frontend + +The documented user-facing frontend is now explicitly: + +- `./bin/fruix system ...` + +This includes the currently supported deployment-oriented actions: + +- `build` +- `rootfs` +- `image` +- `install` +- `installer` +- `installer-iso` + +### Canonical deployment model + +The workflow document now defines Fruix's current deployment model as: + +1. declare a system in Scheme +2. build the system closure in `/frx/store` +3. materialize the artifact appropriate to the target environment +4. boot or install that artifact +5. treat the resulting closure path and emitted provenance metadata as the deployment identity + +### Roll-forward and rollback semantics + +The document makes explicit an important current design point: + +- Fruix rollback is already real at the declaration/closure/deployment layer +- but it is not yet a first-class installed-system generation switch operation + +So the documented rollback workflow today is: + +- retain the earlier declaration +- rebuild or rematerialize it +- redeploy or reboot that earlier closure again + +That matches what Fruix has already validated in earlier phases. + +### Platform-specific installer target-device detail + +The workflow document also records the now-important target-device distinctions between validated environments: + +- installer disk-image path under QEMU: + - `/dev/vtbd1` +- installer ISO path under QEMU: + - `/dev/vtbd0` +- installer ISO path under XCP-ng: + - `/dev/ada0` + +That makes the deployment story less harness-specific and more operator-explicit. + +## Why this satisfies Phase 19.1 + +Before this phase, Fruix already had the machinery for: + +- building declarative system closures +- generating bootable images +- performing direct non-interactive installation +- booting a Fruix installer environment +- booting and installing from a Fruix installer ISO +- rollback-friendly redeploy of earlier declarations + +What was missing was a repo-level explanation that unified those into a single operator workflow. + +The new document closes that gap by connecting: + +- Phase 10 command-surface work +- Phase 15 redeploy/rollback validation +- Phase 18 install and installer-media validation +- and the recent QEMU + XCP-ng installer ISO validation + +## Current boundaries now made explicit + +The documentation intentionally records what Fruix has **not** solved yet: + +- installed-system generation links +- explicit rollback targets and generation metadata +- a first-class `switch` or `reconfigure` command +- installed-system rollback as an in-place operator workflow +- GC-root management for installed systems + +Those are left for later Phase 19 steps rather than being blurred into the current deployment story. + +## References to existing validation + +The documented workflow rests on already-passing validation paths, including: + +- `PASS phase18-system-install` +- `PASS phase18-installer-environment` +- `PASS phase18-installer-iso` +- `PASS phase18-installer-iso-xcpng` +- `PASS phase15-base-rollback-qemu` +- `PASS phase15-base-rollback-xcpng` + +## Conclusion + +Phase 19.1 is now complete. + +Fruix has a documented canonical deployment workflow for FreeBSD covering: + +- build +- image generation +- direct install +- installer-media install +- roll-forward +- rollback by redeploying an earlier declaration + +The next step is Phase 19.2: + +- model installed-system generations, rollback targets, and deployment roots more explicitly. diff --git a/docs/system-deployment-workflow.md b/docs/system-deployment-workflow.md new file mode 100644 index 0000000..fac2d94 --- /dev/null +++ b/docs/system-deployment-workflow.md @@ -0,0 +1,362 @@ +# Fruix system deployment workflow + +Date: 2026-04-04 + +## Purpose + +This document defines the current canonical Fruix workflow for: + +- building a declarative system closure +- materializing deployable artifacts +- installing a declarative system onto an image or disk +- booting through installer media +- rolling forward to a candidate system +- rolling back to an earlier declared system + +This is the Phase 19.1 operator-facing view of the system model already implemented in earlier phases. + +## Core model + +A Fruix system workflow starts from a Scheme file that binds an `operating-system` object. + +Today, the canonical frontend is: + +- `./bin/fruix system ...` + +The important output objects are: + +- **system closure** + - a content-addressed store item under `/frx/store/*-fruix-system-` + - includes boot assets, activation logic, profile tree, metadata, and references +- **rootfs tree** + - a materialized runtime tree for inspection or image staging +- **disk image** + - a bootable GPT/UEFI raw disk image +- **installer image** + - a bootable Fruix installer disk image that installs a selected target system from inside the guest +- **installer ISO** + - a bootable UEFI ISO with an embedded installer mdroot payload +- **install metadata** + - `/var/lib/fruix/install.scm` on installed targets + - records the selected closure path, install spec, and referenced store items including source provenance + +The current deployment story is therefore already declaration-driven and content-addressed, even before first-class installed-system generations are modeled more explicitly. + +## Canonical command surface + +### Build a system closure + +```sh +sudo env HOME="$HOME" \ + GUILE_AUTO_COMPILE=0 \ + GUIX_SOURCE_DIR="$HOME/repos/guix" \ + GUILE_BIN="/tmp/guile-freebsd-validate-install/bin/guile" \ + GUILE_EXTRA_PREFIX="/tmp/guile-gnutls-freebsd-validate-install" \ + SHEPHERD_PREFIX="/tmp/shepherd-freebsd-validate-install" \ + ./bin/fruix system build path/to/system.scm --system my-operating-system +``` + +Primary result: + +- `closure_path=/frx/store/...-fruix-system-...` + +Use this when you want to: + +- validate the declarative system composition itself +- inspect provenance/layout metadata +- compare candidate and current closure paths +- drive later rootfs/image/install steps from the same declaration + +### Materialize a rootfs tree + +```sh +sudo env HOME="$HOME" ... \ + ./bin/fruix system rootfs path/to/system.scm ./rootfs --system my-operating-system +``` + +Primary result: + +- `rootfs=...` +- `closure_path=/frx/store/...` + +Use this when you want to: + +- inspect the runtime filesystem layout directly +- stage a tree for debugging +- validate `/run/current-system`-style symlink layout without booting a full image + +### Materialize a bootable disk image + +```sh +sudo env HOME="$HOME" ... \ + ./bin/fruix system image path/to/system.scm \ + --system my-operating-system \ + --root-size 6g +``` + +Primary result: + +- `disk_image=/frx/store/.../disk.img` + +Use this when you want to: + +- boot the system directly as a VM image +- test a candidate deployment under QEMU or XCP-ng +- validate a roll-forward or rollback candidate by image boot + +### Install directly to an image file or block device + +```sh +sudo env HOME="$HOME" ... \ + ./bin/fruix system install path/to/system.scm \ + --system my-operating-system \ + --target ./installed.img \ + --disk-capacity 12g \ + --root-size 10g +``` + +Primary result: + +- `target=...` +- `target_kind=raw-file` or `block-device` +- `install_metadata_path=/var/lib/fruix/install.scm` + +Use this when you want to: + +- produce an installed target image without booting an installer guest +- validate installation mechanics directly +- populate a raw image or a real `/dev/...` target + +### Materialize a bootable installer disk image + +```sh +sudo env HOME="$HOME" ... \ + ./bin/fruix system installer path/to/system.scm \ + --system my-operating-system \ + --install-target-device /dev/vtbd1 \ + --root-size 10g +``` + +Primary result: + +- `installer_disk_image=/frx/store/.../disk.img` + +Use this when you want to: + +- boot a Fruix installer environment as a disk image +- let the in-guest installer partition and install onto a second disk +- validate non-interactive installation from inside a booted Fruix guest + +### Materialize a bootable installer ISO + +```sh +sudo env HOME="$HOME" ... \ + ./bin/fruix system installer-iso path/to/system.scm \ + --system my-operating-system \ + --install-target-device /dev/vtbd0 +``` + +Primary result: + +- `iso_image=/frx/store/.../installer.iso` +- `boot_efi_image=/frx/store/.../efiboot.img` +- `root_image=/frx/store/.../root.img` + +Use this when you want to: + +- boot through UEFI ISO media instead of a writable installer disk image +- install from an ISO-attached Fruix environment +- test the same install model on more realistic VM paths + +## Deployment patterns + +### 1. Build-first workflow + +The default Fruix operator workflow starts by building the closure first: + +1. edit the system declaration +2. run `fruix system build` +3. inspect emitted metadata +4. if needed, produce one of: + - `rootfs` + - `image` + - `install` + - `installer` + - `installer-iso` + +This keeps the declaration-to-closure boundary explicit. + +### 2. VM image deployment workflow + +Use this when you want to boot a system directly rather than through an installer. + +1. run `fruix system image` +2. boot the image in QEMU or convert/import it for XCP-ng +3. validate: + - `/run/current-system` + - shepherd/sshd state + - activation log +4. keep the closure path from the build metadata as the deployment identity + +This is the current canonical direct deployment path for already-built images. + +### 3. Direct installation workflow + +Use this when you want an installed target image or disk without a booted installer guest. + +1. run `fruix system install --target ...` +2. let Fruix partition, format, populate, and install the target +3. boot the installed result +4. validate `/var/lib/fruix/install.scm` and target services + +This is the most direct install path. + +### 4. Installer-environment workflow + +Use this when the install itself should happen from inside a booted Fruix environment. + +1. run `fruix system installer` +2. boot the installer disk image +3. let the in-guest installer run onto the selected target device +4. boot the installed target + +This is useful when the installer environment itself is part of what needs validation. + +### 5. Installer-ISO workflow + +Use this when the desired operator artifact is a bootable UEFI ISO. + +1. run `fruix system installer-iso` +2. boot the ISO under the target virtualization path +3. let the in-guest installer run onto the selected target device +4. eject the ISO and reboot the installed target + +This is now validated on both: + +- local `QEMU/UEFI/TCG` +- the approved real `XCP-ng` VM path + +## Install-target device conventions + +The install target device is not identical across all boot styles. + +Current validated defaults are: + +- direct installer disk-image path under QEMU: + - `/dev/vtbd1` +- installer ISO path under QEMU: + - `/dev/vtbd0` +- installer ISO path under XCP-ng: + - `/dev/ada0` + +Therefore the canonical workflow is: + +- always treat `--install-target-device` as an explicit deployment parameter when moving between virtualization environments + +Do not assume that a device name validated in one harness is portable to another. + +## Roll-forward workflow + +The current Fruix roll-forward model is declaration-driven. + +Canonical process: + +1. keep the current known-good system declaration +2. prepare a candidate declaration + - this may differ by FreeBSD base identity + - source revision + - services + - users/groups + - or other operating-system fields +3. run `fruix system build` for the candidate +4. materialize either: + - `fruix system image` + - `fruix system install` + - `fruix system installer` + - `fruix system installer-iso` +5. boot or install the candidate +6. validate the candidate closure in the booted system + +The important property is that the candidate closure appears beside the earlier one in `/frx/store` rather than mutating it in place. + +## Rollback workflow + +The current canonical rollback workflow is also declaration-driven. + +Today, rollback means: + +1. retain the earlier declaration that produced the known-good closure +2. rebuild or rematerialize that earlier declaration +3. redeploy or reboot that earlier artifact again + +Concretely, the usual rollback choices are: + +- rebuild the earlier declaration with `fruix system build` and confirm the old closure path reappears +- boot the earlier declaration again through `fruix system image` +- reinstall the earlier declaration through `fruix system install`, `installer`, or `installer-iso` if the deployment medium itself must change + +This rollback story has already been validated at the closure/image/deployment level: + +- side-by-side base-version coexistence in `/frx/store` +- roll-forward to a candidate closure +- rollback by rebuilding and booting the earlier declaration again +- validation on both local QEMU and the approved XCP-ng VM path + +### Important scope note + +This is not yet the same thing as a first-class installed-system generation switch command. + +Current rollback is: + +- **redeploy the earlier declaration again** + +Future Phase 19 work is expected to make these more explicit: + +- current generation +- previous generation +- rollback target +- installed-system roots and generation links +- an operator-facing installed-system rollback workflow + +## Provenance and deployment identity + +For any serious deployment or rollback decision, the canonical identity is not merely the host name. It is the emitted metadata: + +- `closure_path` +- declared FreeBSD base/source metadata +- materialized source store paths +- install metadata at `/var/lib/fruix/install.scm` +- store item counts and reference lists + +Operators should retain metadata from successful candidate and current deployments because Fruix already emits enough data to answer: + +- which declaration was built +- which closure booted +- which source snapshot was materialized +- which target device or image was installed + +## Current limitations + +The deployment workflow is now coherent, but it is not yet the final generation-management story. + +Not yet first-class: + +- installed-system generation directories and symlink model +- a dedicated `switch` or `reconfigure` command +- an installed-system rollback command that moves between generations in place +- explicit GC-root management for installed systems + +Those are the next logical steps after Phase 19.1. + +## Summary + +The current canonical Fruix deployment model is: + +- **declare** a system in Scheme +- **build** the closure with `fruix system build` +- **materialize** the artifact appropriate to the deployment target +- **boot or install** that artifact +- **identify deployments by closure path and provenance metadata** +- **roll back by rebuilding/redeploying the earlier declaration**, not by mutating the current closure in place + +That is the operator-facing workflow Fruix should document and use until explicit installed-system generation management is added.