Files

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

  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.