9.5 KiB
Plan 5: split Fruix bootstrap from canonical Fruix
This plan turns the boundary from docs/bootstrap.md into a concrete migration sequence.
The intended end state is:
fruix-bootstrapprepares a plain FreeBSD host to act as a Fruix builderfruixis the canonical repo/channel for:- packages
- systems
- installers
- deployment
- node lifecycle
- a booted Fruix system no longer depends on
fruix-bootstrap
Primary goal
Reach the point where a plain FreeBSD machine can:
- use
fruix-bootstrapto become a Fruix builder - use that builder to evaluate a pinned
fruixcheckout or revision - build a Fruix artifact from
fruix- installer ISO
- VM image
- system closure
- boot or install that artifact
- continue operating using only
fruix
Guiding rules
fruixis the only canonical home for long-lived Fruix logic.fruix-bootstrapmay wrapfruix, but should not fork it.- If a booted Fruix node should understand it, it belongs in
fruix. - Bootstrap-only foreign-host glue belongs in
fruix-bootstrap. - Prefer temporary wrappers over duplicated logic.
What should move to fruix first
These are the highest-priority items to live in fruix, because they define the product rather than the bootstrap path.
1. Package definitions
Move first:
modules/fruix/packages/...
Why:
- package definitions are canonical Fruix content
- they are part of the future channel story
- booted Fruix systems should use these directly
2. FreeBSD system model and system artifact logic
Move early:
modules/fruix/system/freebsd/model.scmmodules/fruix/system/freebsd/source.scmmodules/fruix/system/freebsd/build.scmmodules/fruix/system/freebsd/render.scmmodules/fruix/system/freebsd/media.scmmodules/fruix/system/freebsd/utils.scmmodules/fruix/system/freebsd/executor.scm- the thin facade
modules/fruix/system/freebsd.scm
Why:
- these define what a Fruix system is
- they define build/promotion/deploy behavior
- they must remain stable across bootstrap and post-bootstrap operation
3. Fruix CLI semantics
Move early:
scripts/fruix.scm- the long-lived CLI behavior it implements
Why:
- command semantics belong to Fruix, not bootstrap
- installed nodes should match the host CLI model
4. Installed-node lifecycle logic
Move early:
- node helper/module tree logic
buildbuild-basedeployreconfigurestatusswitchrollback
Why:
- these are core Fruix product actions after first boot
5. Installer and future TUI installer
Move early and keep there:
- installer environment logic
- installer ISO logic
- future TUI installer application
Why:
- the installer is a Fruix product artifact
- Fruix should be able to rebuild its own installer
6. Metadata formats
Move early and treat as canonical:
- system generation metadata
- deployment metadata
- promoted native-build result metadata
- any future Fruix revision/pin metadata
Why:
- metadata is part of Fruix identity
- bootstrap should consume these formats, not redefine them
What can temporarily remain in fruix-bootstrap
These items are allowed to remain here while the split is in progress.
1. Bootstrap wrapper entrypoints
Examples:
- shell wrappers that locate the pinned
fruixcheckout - shell wrappers that set
GUILE_LOAD_PATH,GUILE_PREFIX, etc. - entrypoints that invoke the real Fruix CLI from the foreign host
These should become thin wrappers around fruix, not alternate implementations.
2. Foreign-host dependency bring-up
Examples:
- locating or building Guile
- locating or building Shepherd support pieces
- preparing the host environment so the Fruix CLI can run
This is bootstrap territory.
3. Foreign-host validation harnesses
Examples:
- tests for plain-FreeBSD -> Fruix-builder bring-up
- tests that validate local bootstrap assumptions
- docs for first-time setup on vanilla FreeBSD
These stay useful here even after most Fruix logic moves out.
4. Temporary compatibility glue
Examples:
- wrappers that still point from bootstrap into
../fruix - temporary environment shims needed while the split settles
These are acceptable if they are clearly transitional and do not redefine Fruix semantics.
What should not remain in fruix-bootstrap
Do not let this repo become a second home for:
- canonical package definitions
- canonical system semantics
- deployment semantics
- installer semantics
- long-lived metadata definitions
- installed-node behavior after first boot
If bootstrap needs those capabilities, it should invoke them from fruix.
Proposed migration sequence
Milestone 1: make fruix-bootstrap call fruix
Goal:
- this repo stops being the canonical implementation and becomes a launcher/bootstrap layer
Deliverables:
- define a pinned
fruixcheckout input for bootstrap- initially a local path such as
../fruixis acceptable
- initially a local path such as
- bootstrap wrappers invoke Fruix modules/scripts from that checkout
- package/system logic is no longer edited here as the primary source of truth
Success signal:
- running the bootstrap entrypoint clearly uses
../fruixas the canonical Fruix source
Milestone 2: move canonical modules into fruix
Goal:
- all long-lived package/system logic lives in
fruix
Deliverables:
modules/fruix/packages/...live infruixmodules/fruix/system/...live infruixscripts/fruix.scmlives infruix- this repo only wraps/invokes those files
Success signal:
- the same Fruix code is used both:
- from a foreign host via bootstrap
- from a booted Fruix node
Milestone 3: record a pinned Fruix identity in artifacts
Goal:
- built artifacts know which Fruix revision produced them
Deliverables:
- define minimal Fruix identity metadata, such as:
- checkout path for local development
- git commit when available
- optional dirty/clean marker
- record that identity in:
- image metadata
- installer metadata
- deployed generation metadata
- installed-node metadata
Success signal:
- a system can answer: “which Fruix revision built me?”
Milestone 4: first self-contained Fruix install from bootstrap
Goal:
- prove that bootstrap is needed only to create the first Fruix artifact
Deliverables:
- from plain FreeBSD + bootstrap, build either:
- a Fruix installer ISO, or
- a Fruix VM image
- boot/install the resulting system
- validate that the running Fruix node can perform post-boot actions using
fruixonly:fruix system buildfruix system build-basefruix system reconfigurefruix system deployfruix system rollback
Success signal:
- no runtime dependence on the bootstrap checkout is required after first boot
Milestone 5: build the first TUI installer in fruix
Goal:
- the user-facing installation experience is defined by Fruix itself
Deliverables:
- minimal text/TUI installer application in
fruix - built into the Fruix installer environment / installer ISO
- opinionated input surface, for example:
- target disk
- hostname
- root SSH key or password
- optional operator user
- optional profile/template selection
- generated declaration and install metadata recorded by Fruix
Success signal:
- bootstrap builds the installer, but Fruix defines the installer behavior
First split-validation milestone
The first major proof should be this:
Bootstrap builds Fruix, then Fruix continues alone
Concretely:
- start from plain FreeBSD
- use
fruix-bootstrapto create a Fruix builder - point it at pinned
../fruix - build a Fruix VM image or installer ISO from
fruix - boot the resulting Fruix system
- on the running Fruix system, perform at least:
fruix system buildfruix system reconfigurefruix system rollback
- verify none of those steps need the bootstrap checkout
This is the first meaningful architectural victory of the split.
Recommended immediate tasks
- add a bootstrap configuration variable for the pinned Fruix checkout
- initially defaulting to
../fruix
- initially defaulting to
- change bootstrap wrappers to load Fruix code from that checkout
- stop treating this repo as the canonical edit location for Fruix modules
- move package/system/CLI logic into
fruix - add minimal Fruix revision metadata recording to built artifacts
- then proceed to the Fruix-defined TUI installer
Temporary practical compromises
The split does not require perfection on day one.
Temporarily acceptable:
- local-path pinning to
../fruix - wrapper scripts in bootstrap that export environment variables for Fruix
- some bootstrap-era host assumptions while the builder path stabilizes
Not acceptable long-term:
- duplicated package definitions in both repos
- duplicated system semantics in both repos
- booted Fruix nodes depending on bootstrap logic to keep operating
Open questions to resolve later
These do not block the boundary itself:
- exact Fruix channel/lock file format
- exact user-facing
upgradepolicy - binary publication/substitution design
- local
jailexecutor for native base builds
Those are important, but they should be built on top of the split rather than baked into the boundary prematurely.
Summary
The migration priority is:
- make bootstrap a thin launcher for pinned
fruix - move canonical logic into
fruix - record Fruix identity in artifacts
- prove a booted Fruix node can continue without bootstrap
- build the TUI installer in
fruix
If this plan is followed, fruix-bootstrap stays small and generic, while fruix becomes the real long-term system source of truth.