# Fruix on FreeBSD, Path D: Declarative Source Acquisition, Installation Artifacts, and the Road to Self-Hosting This document extends `docs/PLAN_3.md` after the completion of Phases 1 through 15. The project has now crossed another important threshold: - Fruix builds native FreeBSD base artifacts from `/usr/src` into `/frx/store` - the validated boot/runtime path is host-base-free at the deployed system layer - the FreeBSD base is now an explicit declarative Fruix input - side-by-side base versions can coexist in `/frx/store` - Fruix can rebuild, redeploy, and roll back between declared base versions That means the next question is no longer whether Fruix can assemble and boot a native FreeBSD system. It can. The next question is how to make that native base path **more reproducible, more source-declarative, and more installable**. The biggest remaining impurity is now clear: - the native base build path still relies on the builder host's ambient `/usr/src` That was the right bridge through Phases 13 to 15, but it should not be the long-term source story. Accordingly, Plan 4 focuses on three connected goals: 1. make FreeBSD source acquisition and source identity more declarative 2. turn the current image-generation path into a real installation story 3. only then move carefully toward self-hosted base builds This preserves the Guix-inspired core while continuing to embrace FreeBSD semantics rather than trying to imitate Linux-specific workflows. Throughout this plan, the canonical Fruix roots remain: - `/frx/store` - `/frx/var` - `/frx/etc` The user-facing system remains **Fruix**, with CLI `fruix`, while internal upstream-derived names continue to be renamed selectively rather than mechanically. The current real-VM validation constraints still apply unless explicitly relaxed later: - approved VM: `90490f2e-e8fc-4b7a-388e-5c26f0157289` - approved A/B VDIs: - `0f1f90d3-48ca-4fa2-91d8-fc6339b95743` - `7061d761-3639-4bec-87f7-2ba1af924eaa` --- ## Current State at the Start of Plan 4 Fruix on FreeBSD already has: - a functioning content-addressed store and derivation path under `/frx/store` - daemon-mediated builds with validated FreeBSD isolation concepts - a working declarative FreeBSD operating-system model - validated system closure, rootfs, and image generation through `fruix system ...` - real QEMU and XCP-ng boot validation - a host-base-free native boot/runtime composition built from: - `freebsd-native-kernel` - `freebsd-native-bootloader` - `freebsd-native-runtime` - a declarative `freebsd-base` model that records: - base identity - version label - branch - source root - target and kernel configuration - side-by-side native base versions in `/frx/store` - rollback-friendly redeploy across those declared base versions - a documented decision to continue using host-built native base artifacts for now rather than jumping immediately to guest self-hosting The main remaining architectural compromise is now not the runtime/boot artifact split, but the **source boundary**: - native FreeBSD base builds still use ambient `/usr/src` - source acquisition is not yet a first-class Fruix-managed input - therefore reproducibility and provenance still stop short of a fully declared source story At the same time, Fruix still lacks a real installation workflow beyond image generation and validation harnesses. --- ## Guiding Decision for the Next Milestone The next major milestone should be: - **declarative FreeBSD source acquisition and source pinning first**, - then **native base builds from fetched or materialized source inputs**, - then a **real installation story**, - and only after that, a controlled re-evaluation of self-hosted base builds. This means the next work should optimize for: - explicit source identity - stronger provenance - side-by-side source revisions - repeatable rebuilds from fetched or materialized source trees - installation artifacts that are operator-usable beyond current validation harnesses It should **not** optimize for: - jumping straight into guest self-hosting while source identity is still weak - building a large interactive installer before the installation primitives are clear - replacing the current validated host-built native-base path prematurely --- ## Phase 16: Make FreeBSD Source Acquisition Declarative This is the next architectural pivot after the native base/runtime split. The goal is to move from: - “build from whatever `/usr/src` is on the host” into: - “build from a declared FreeBSD source input with explicit provenance.” ### Intermediate Goal 16.1: Model FreeBSD Source Inputs Explicitly **Verification Goal 16.1:** Introduce a first-class Fruix model for FreeBSD source inputs. This can be a new `freebsd-source` record, an extension of `freebsd-base`, or another explicit source object, but it should represent at least: - source kind: - local checkout snapshot - fetched Git checkout - fetched `src.txz` archive - source URL or local path - branch or release line - commit, tag, or revision when applicable - expected tree or archive hash - any intended patch queue or source transformation layer This step should also document how that source object relates to: - `freebsd-base` - native kernel/world/runtime/bootloader/headers builds - output identity in `/frx/store` **Success Criteria:** Fruix can describe FreeBSD source inputs as explicit declared objects rather than only relying on ambient `/usr/src`. ### Intermediate Goal 16.2: Fetch or Materialize Source Trees Under Fruix Control **Verification Goal 16.2:** Materialize a clean FreeBSD source tree from a declared source input using Fruix-managed fetch/materialization logic. The first implementation may be intentionally modest, such as: - fetching a Git checkout at a pinned revision from `https://git.FreeBSD.org/src.git` - or downloading a `src.txz` archive and verifying its hash - for example from `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz` - or from snapshot paths such as `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz` - or snapshotting a local source tree into a content-addressed source artifact with explicit metadata This phase should also define where downloaded or temporary source material lives, for example under: - `/frx/var/cache/fruix/freebsd-source` The important point is that the eventual build input should no longer be “the current mutable host tree” but a **materialized source snapshot with recorded identity**. **Success Criteria:** Fruix can fetch or materialize a pinned FreeBSD source tree with recorded provenance and a stable identity suitable for native base builds. ### Intermediate Goal 16.3: Build Native Base Artifacts From the Declared Source Input **Verification Goal 16.3:** Teach the native FreeBSD base build path to consume the declared source artifact rather than ambient `/usr/src`. This should preserve the validated Plan-3/Plan-4 deployment story: - native kernel - native bootloader slice - native runtime slice - host-base-free validated path The goal is not yet to change the successful boot/runtime model, only the **source input boundary**. **Success Criteria:** Fruix builds native FreeBSD base artifacts from a declared source input, and the resulting store artifacts record that source identity explicitly. --- ## Phase 17: Support Multiple FreeBSD Source Revisions Side by Side Once source acquisition is explicit, Fruix should prove that it can treat source revisions the way it already treats declared base versions: as side-by-side inputs rather than in-place mutations. ### Intermediate Goal 17.1: Support Side-by-Side FreeBSD Source Revisions in `/frx/store` **Verification Goal 17.1:** Demonstrate that at least two distinct declared FreeBSD source inputs can coexist and produce distinct native base artifacts in `/frx/store`. These can differ by: - Git revision - source archive hash - local source snapshot hash - or another explicit source identity boundary The goal is to prove that the source declaration, not ambient host state, now drives the base build outputs. **Success Criteria:** Fruix can hold at least two distinct FreeBSD source revisions side by side as meaningful native base inputs and outputs. ### Intermediate Goal 17.2: Rebuild and Boot From Two Distinct Source Revisions **Verification Goal 17.2:** Build and boot systems from at least two distinct declared source revisions using the validated native base path. This should preserve the current validation strategy: - local QEMU/UEFI/TCG where useful - real XCP-ng validation on the approved VM and approved A/B VDI path The booted systems do not necessarily need to differ in obvious runtime behavior; the important point is that their source identity and native base outputs differ and are tracked correctly. **Success Criteria:** Fruix boots systems built from two distinct declared FreeBSD source revisions and records those revisions in system metadata. ### Intermediate Goal 17.3: Clarify Source Provenance, Caching, and Update Policy **Verification Goal 17.3:** Document and encode the intended policy for: - source caching - source refresh/invalidation - patch application if any - tree hashing rules - when a new source identity should or should not invalidate native base outputs This step should reduce ambiguity before installation artifacts and later self-hosting experiments depend on these source objects. **Success Criteria:** the repo clearly explains how FreeBSD source objects are fetched, cached, identified, invalidated, and consumed by native base builds. --- ## Phase 18: Turn System Images Into a Real Installation Story Fruix can already produce bootable images. The next step is to make installation itself a first-class Fruix story rather than a manual consequence of image generation. ### Intermediate Goal 18.1: Define a Minimal Non-Interactive Installation Flow **Verification Goal 18.1:** Introduce a minimal installation workflow that can take a Fruix system artifact and install it to a target disk or image in a repeatable way. The first version should prefer clarity and automation over interactivity. For example, it may: - partition a target disk - create required filesystems - copy or materialize the selected system closure - install boot assets - stage activation/runtime metadata - configure root SSH key or basic operator access This first step should be VM-friendly and serial-console-friendly. **Success Criteria:** Fruix can perform a repeatable installation of a declarative system onto a target disk or image without relying on ad hoc manual assembly. ### Intermediate Goal 18.2: Produce a Minimal Installer Environment **Verification Goal 18.2:** Produce a small installer environment that can boot into a Fruix-managed install context and perform the installation workflow. This environment may initially be one of: - a special-purpose disk image - a minimal recovery/install rootfs - a proto-installer medium with only the tools needed for partitioning, filesystem creation, and deployment The target here is not a polished live environment. It is a reliable installer substrate. **Success Criteria:** Fruix can boot a minimal installer environment and use it to install a selected Fruix system to a target disk. ### Intermediate Goal 18.3: Build a Bootable Installer ISO **Verification Goal 18.3:** Produce a bootable installer ISO for UEFI systems. The first ISO can be intentionally narrow in scope. It should be enough to: - boot under QEMU and, where practical, on the validated virtualization path - expose the install workflow - install a target Fruix system - leave the machine bootable into the installed system A polished graphical or highly interactive installer is not required here. **Success Criteria:** Fruix can build a bootable installer ISO that installs a declarative Fruix-on-FreeBSD system. --- ## Phase 19: Strengthen System Deployment and Generation Management The current deployment model already has the important Guix-like semantic properties at the closure/store layer. This phase is about making that story more operator-facing and less harness-specific. ### Intermediate Goal 19.1: Define a First-Class Fruix Deployment Workflow **Verification Goal 19.1:** Define and document the canonical user-facing deployment workflow for system rebuild, image generation, installation, and rollback. This may introduce or refine user-facing commands such as: - `fruix system build` - `fruix system image` - future deployment/install/switch subcommands if justified The key goal is clarity: operators should have an obvious Fruix way to move between generations and deployments. **Success Criteria:** the repo documents a coherent user-facing deployment workflow for system build, install, roll-forward, and rollback. ### Intermediate Goal 19.2: Model System Generations More Explicitly **Verification Goal 19.2:** Make the system-generation story more explicit in metadata and deployment roots. This should include deciding how Fruix wants to represent: - current system generation - previous system generation - rollback target - GC roots associated with installed systems This does not have to copy Guix System mechanically, but it should preserve the same important properties. **Success Criteria:** Fruix has a clearer model for installed system generations and rollback roots rather than relying mainly on test-harness knowledge. ### Intermediate Goal 19.3: Validate Installed-System Rollback as an Operator Workflow **Verification Goal 19.3:** Validate rollback through the intended installed-system workflow, not only through build/image test harnesses. This step should prove that the installation and generation model work together coherently. **Success Criteria:** an installed Fruix system can move between generations using the intended operator-facing deployment model. --- ## Phase 20: Controlled Steps Toward Self-Hosted Base Builds Only after source identity and installation/deployment boundaries are stronger should Fruix seriously revisit self-hosted base builds. ### Intermediate Goal 20.1: Validate a Fruix-Managed Development Environment for Native Base Work **Verification Goal 20.1:** Ensure that a Fruix-managed system can expose the development/runtime/toolchain environment needed for deeper FreeBSD-native build work. This should build on the cleaner runtime/development split already established in Phase 14. The goal is not yet full self-hosting; it is to prove that the system can host the tools and profiles needed for that work in a controlled way. **Success Criteria:** a Fruix-managed system can expose a usable development environment for native FreeBSD build tasks. ### Intermediate Goal 20.2: Run Host-Initiated Native Base Builds Inside a Fruix-Managed Environment **Verification Goal 20.2:** As an intermediate step, perform native base builds inside a Fruix-managed environment or jail while still using the host as the outer orchestrator. This narrows the remaining gap without immediately demanding full guest self-hosting. **Success Criteria:** Fruix can build native FreeBSD base artifacts from inside a Fruix-managed build environment, with the host still orchestrating the outer loop. ### Intermediate Goal 20.3: Reassess and Potentially Prototype Guest Self-Hosted Base Builds **Verification Goal 20.3:** Revisit guest self-hosting only after the earlier source/install/deployment goals are complete enough to make that experiment meaningful. At that point, the question should be answered with real evidence: - what exactly self-hosting would improve - what it would cost in complexity - how it would fit with the source/deployment model already established **Success Criteria:** the project either: - validates a first controlled guest self-hosted base build, or - records a clear evidence-based decision to continue preferring host-orchestrated native builds. --- ## Strategic Notes ### Why this order? This sequence is intended to preserve the most important win from Plan 3 and Phase 15: - Fruix already has a native, store-based, rollback-friendly FreeBSD base path The next improvement should therefore be to make the **source input** as explicit as the **deployment output** already is. After that, Fruix can turn its successful image-generation path into a real installation story. Only then will self-hosting be judged in the right context. ### Why not jump straight to self-hosting? Because at the end of Phase 15, the biggest remaining weakness is not “where the build runs”, but “how explicitly the source is declared and acquired”. If self-hosting were attempted immediately, it would mix together: - source acquisition problems - toolchain/profile maturity problems - build-environment questions - deployment/installation questions - and virtualization/operator constraints That would make debugging and design less clear. ### Why add installation work before self-hosting? Because Fruix should become more usable as a system even if self-hosting remains a later milestone. A real installation story would: - make the current system model more operator-meaningful - improve validation of generation/deployment semantics - prepare the project for broader VM and eventually hardware use - make later self-hosting experiments easier to stage and reproduce ### What success will mean Success under this plan means the project moves from: - “Fruix builds native FreeBSD base artifacts from the host’s current `/usr/src` and boots them successfully” into: - “Fruix acquires FreeBSD sources declaratively, builds native base artifacts from pinned source inputs, installs systems through a real Fruix workflow, and approaches self-hosting from a much cleaner architectural position.”