# 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.