Files

358 lines
9.5 KiB
Markdown

# 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-bootstrap` prepares a plain FreeBSD host to act as a **Fruix builder**
- `fruix` is 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:
1. use `fruix-bootstrap` to become a Fruix builder
2. use that builder to evaluate a pinned `fruix` checkout or revision
3. build a Fruix artifact from `fruix`
- installer ISO
- VM image
- system closure
4. boot or install that artifact
5. continue operating using only `fruix`
## Guiding rules
1. `fruix` is the only canonical home for long-lived Fruix logic.
2. `fruix-bootstrap` may wrap `fruix`, but should not fork it.
3. If a booted Fruix node should understand it, it belongs in `fruix`.
4. Bootstrap-only foreign-host glue belongs in `fruix-bootstrap`.
5. 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.scm`
- `modules/fruix/system/freebsd/source.scm`
- `modules/fruix/system/freebsd/build.scm`
- `modules/fruix/system/freebsd/render.scm`
- `modules/fruix/system/freebsd/media.scm`
- `modules/fruix/system/freebsd/utils.scm`
- `modules/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
- `build`
- `build-base`
- `deploy`
- `reconfigure`
- `status`
- `switch`
- `rollback`
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 `fruix` checkout
- 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 `fruix` checkout input for bootstrap
- initially a local path such as `../fruix` is acceptable
- 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 `../fruix` as 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 in `fruix`
- `modules/fruix/system/...` live in `fruix`
- `scripts/fruix.scm` lives in `fruix`
- 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 `fruix` only:
- `fruix system build`
- `fruix system build-base`
- `fruix system reconfigure`
- `fruix system deploy`
- `fruix 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:
1. start from plain FreeBSD
2. use `fruix-bootstrap` to create a Fruix builder
3. point it at pinned `../fruix`
4. build a Fruix VM image or installer ISO from `fruix`
5. boot the resulting Fruix system
6. on the running Fruix system, perform at least:
- `fruix system build`
- `fruix system reconfigure`
- `fruix system rollback`
7. verify none of those steps need the bootstrap checkout
This is the first meaningful architectural victory of the split.
## Recommended immediate tasks
1. add a bootstrap configuration variable for the pinned Fruix checkout
- initially defaulting to `../fruix`
2. change bootstrap wrappers to load Fruix code from that checkout
3. stop treating this repo as the canonical edit location for Fruix modules
4. move package/system/CLI logic into `fruix`
5. add minimal Fruix revision metadata recording to built artifacts
6. 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 `upgrade` policy
- binary publication/substitution design
- local `jail` executor 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:
1. make bootstrap a thin launcher for pinned `fruix`
2. move canonical logic into `fruix`
3. record Fruix identity in artifacts
4. prove a booted Fruix node can continue without bootstrap
5. 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.