Model declarative FreeBSD source inputs

This commit is contained in:
2026-04-03 11:47:52 +02:00
parent 390bfb248f
commit d89225fe11
9 changed files with 1213 additions and 4 deletions

406
docs/PLAN_4.md Normal file
View File

@@ -0,0 +1,406 @@
# 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 hosts 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.”

View File

@@ -1,5 +1,83 @@
# Progress
## 2026-04-03 — Phase 16.1 completed: FreeBSD source inputs are now explicit Fruix objects
Completed work:
- added `docs/PLAN_4.md` to define the post-Phase-15 roadmap around:
- declarative FreeBSD source acquisition
- installation artifacts
- the controlled path toward self-hosting
- introduced a first-class `freebsd-source` record in `modules/fruix/packages/freebsd.scm` with:
- supported kinds:
- `local-tree`
- `git`
- `src-txz`
- exported accessors for:
- `name`
- `kind`
- `url`
- `path`
- `ref`
- `commit`
- `sha256`
- new `%default-freebsd-source`
- extended `freebsd-base` so it now records both:
- transitional `source-root`
- declarative `source`
- added/exported:
- `freebsd-base-source`
- threaded declared source fields into native package plans so native build outputs can record them
- in `modules/fruix/system/freebsd.scm`:
- added source validation for:
- `local-tree`
- `git`
- `src-txz`
- added `freebsd-source-spec`
- `freebsd-base-spec` now nests the declared source
- native manifests and `.freebsd-native-build-info.scm` now include:
- `declared-source`
- closures now generate:
- `metadata/freebsd-source.scm`
- `metadata/store-layout.scm` now records:
- `freebsd-source`
- in `scripts/fruix.scm`, `build` and `image` metadata now emit:
- `freebsd_source_name`
- `freebsd_source_kind`
- `freebsd_source_url`
- `freebsd_source_path`
- `freebsd_source_ref`
- `freebsd_source_commit`
- `freebsd_source_sha256`
- `freebsd_source_file`
- added validation artifacts:
- `tests/system/phase16-declarative-source-operating-system.scm.in`
- `tests/system/run-phase16-declarative-source-build.sh`
- `tests/system/validate-phase16-freebsd-source.scm`
- compared Fruix's new source model with Guix's source modeling via:
- `~/repos/guix/guix/packages.scm`
- `~/repos/guix/guix/git-download.scm`
- wrote:
- `docs/reports/phase16-declarative-source-model-freebsd.md`
Validation:
- `PASS phase16-declarative-source-build`
- source model probe confirmed support for:
- local-tree `/usr/src`
- Git refs such as `stable/15` at `https://git.FreeBSD.org/src.git`
- canonical `src.txz` URLs such as:
- `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz`
- `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz`
- closure/native metadata now records the declared source explicitly while preserving the current validated `/usr/src` build path
Current assessment:
- Phase 16.1 is complete
- Fruix can now describe FreeBSD source inputs explicitly, but it does not fetch/materialize them yet
- the next step is Phase 16.2:
- fetch or materialize declared FreeBSD source inputs under Fruix control and use their stable identity as the next reproducibility boundary
## 2026-04-01 — Phase 1.1 started: Guile verified on FreeBSD amd64
Completed work:

View File

@@ -0,0 +1,202 @@
# Phase 16.1: model FreeBSD source inputs explicitly
Date: 2026-04-03
## Goal
Phase 16.1 introduces a first-class Fruix model for FreeBSD source inputs so the native base path is no longer described only as "whatever `/usr/src` is on the host".
This step does **not** yet fetch or materialize remote source trees. It makes the source declaration explicit and records it through the package, system, and CLI metadata layers so later phases can replace ambient `/usr/src` with fetched or materialized inputs cleanly.
## Implementation
### New declarative FreeBSD source record
Added in `modules/fruix/packages/freebsd.scm`:
- `freebsd-source`
- `freebsd-source?`
- accessors for:
- `name`
- `kind`
- `url`
- `path`
- `ref`
- `commit`
- `sha256`
- `%default-freebsd-source`
Supported source kinds are now modeled explicitly as:
- `local-tree`
- `git`
- `src-txz`
The default source remains a local-tree declaration for:
- `/usr/src`
### FreeBSD bases now carry a declared source object
Extended `freebsd-base` so it now records:
- the transitional `source-root` still used by the current native build path
- a new `source` record describing the declared FreeBSD source input
Added/exported:
- `freebsd-base-source`
This keeps the validated `/usr/src` path working while giving the base a source object that can later point at fetched Git or `src.txz` materializations.
### Native package plans and metadata now record the declared source
`modules/fruix/packages/freebsd.scm` now threads source fields into native package install plans.
`modules/fruix/system/freebsd.scm` now records a `declared-source` block in:
- native package manifests
- `.freebsd-native-build-info.scm`
This means native kernel, bootloader, and runtime outputs now remember both:
- the declared base
- the declared source input
### Operating-system validation now checks source declarations
Added source validation in `modules/fruix/system/freebsd.scm`.
Current accepted source declarations are:
- `local-tree`
- requires a path
- `git`
- requires a URL and at least a ref or commit
- `src-txz`
- requires a URL
This is intentionally enough for Phase 16.1 modeling without yet enforcing the later fetch/materialization policy.
### Closure metadata now includes a dedicated source file
System closures now generate:
- `metadata/freebsd-source.scm`
and embed source information in:
- `metadata/freebsd-base.scm`
- `metadata/store-layout.scm`
- `parameters.scm`
### CLI metadata now exposes the declared FreeBSD source
`scripts/fruix.scm` now emits the following for `fruix system build` and `image`:
- `freebsd_source_name`
- `freebsd_source_kind`
- `freebsd_source_url`
- `freebsd_source_path`
- `freebsd_source_ref`
- `freebsd_source_commit`
- `freebsd_source_sha256`
- `freebsd_source_file`
## Guix comparison
This follows the same broad idea as Guix source modeling:
- Guix uses `origin` in `guix/packages.scm`
- Git-backed sources are modeled with `git-reference` in `guix/git-download.scm`
Phase 16.1 does not copy Guix mechanically, but it adopts the same useful architectural boundary:
- source identity should be a declarative object
- builds should record that source object explicitly
For Fruix, the source kinds are FreeBSD-oriented:
- local source tree snapshots for development
- FreeBSD Git refs/commits
- official FreeBSD `src.txz` archives
## Upstream source forms verified during this step
Verified usable upstream source forms include:
- Git:
- `https://git.FreeBSD.org/src.git`
- release archive:
- `https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz`
- snapshot archive:
- `https://download.freebsd.org/snapshots/amd64/15.0-STABLE/src.txz`
The shorter release URL form above is treated as the canonical example in this phase.
## New files
Added:
- `docs/PLAN_4.md`
- `tests/system/phase16-declarative-source-operating-system.scm.in`
- `tests/system/run-phase16-declarative-source-build.sh`
- `tests/system/validate-phase16-freebsd-source.scm`
## Validation
Passing run:
- `PASS phase16-declarative-source-build`
- workdir:
- `/tmp/fruix-phase16-declarative-source.0LRvaC`
The source model probe confirmed:
```text
local_kind=local-tree
local_path=/usr/src
git_kind=git
git_url=https://git.FreeBSD.org/src.git
git_ref=stable/15
txz_kind=src-txz
txz_url=https://download.freebsd.org/releases/amd64/15.0-RELEASE/src.txz
txz_sha256=example-sha256
base_source_accessor=ok
```
The closure build confirmed:
```text
closure_path=/frx/store/ac73a2a89c3c3f794462ccde0d9f0952362dc2ae32e631a7e99e07e7363e6118-fruix-system-fruix-freebsd
kernel_store=/frx/store/c2f771a794f94cf168ae26a421ffab33e0ae4765f23882257d5bb7b2947d493d-freebsd-native-kernel-15.0-STABLE-source-model
bootloader_store=/frx/store/a146c9c2fcacdc02454fe3bfd3afb5c5dbea54b6514242d53df0783ef4ee34fd-freebsd-native-bootloader-15.0-STABLE-source-model
runtime_store=/frx/store/530e96440a518821a701db03f9437d7646c0447f8ea55c5df08417e9274dead1-freebsd-native-runtime-15.0-STABLE-source-model
native_base_store_count=3
host_base_store_count=0
freebsd_source_name=host-usr-src
freebsd_source_kind=local-tree
freebsd_source_path=/usr/src
freebsd_source_file=/frx/store/ac73a2a89c3c3f794462ccde0d9f0952362dc2ae32e631a7e99e07e7363e6118-fruix-system-fruix-freebsd/metadata/freebsd-source.scm
declarative_source_model=ok
```
The harness also verified that:
- `metadata/freebsd-source.scm` exists
- `metadata/freebsd-base.scm` contains a nested source block
- `metadata/store-layout.scm` records the declared source explicitly
- native build info files contain a `declared-source` block with:
- `kind . local-tree`
- `path . "/usr/src"`
## Result
Phase 16.1 is complete.
Fruix now has an explicit source declaration layer for FreeBSD bases while preserving the current validated `/usr/src`-driven native build path.
That means Phase 16.2 can focus narrowly on the next real boundary:
- fetching or materializing declared FreeBSD source inputs under Fruix control
- instead of just describing them