361 lines
17 KiB
Markdown
361 lines
17 KiB
Markdown
# Fruix on FreeBSD, Path B: Real Guix/Fruix Integration First, Then a Minimal bhyve System
|
||
|
||
This document extends `docs/PLAN.md` after the completion of Phases 1 through 4 on the current FreeBSD amd64 prototype track. The next major objective is no longer to validate isolated subsystems, but to connect them through a real Guix/Fruix execution path and ultimately boot a minimal Fruix system in bhyve.
|
||
|
||
Unlike a prototype-image-first strategy, this plan takes **Path B directly**:
|
||
|
||
- prioritize real Guix/Fruix checkout and runtime integration on FreeBSD,
|
||
- connect package lowering, derivation generation, daemon/store behavior, and system composition,
|
||
- and only then generate and boot a minimal bhyve image from that integrated path.
|
||
|
||
This is slower than assembling an image from ad hoc prototypes, but it better preserves the architectural goal: a genuine Fruix system built through Guix-style mechanisms rather than merely a FreeBSD image with Fruix-like parts.
|
||
|
||
Throughout this plan, the experimental store root remains:
|
||
|
||
- `/frx/store`
|
||
- `/frx/var`
|
||
- `/frx/etc`
|
||
|
||
rather than `/gnu/store`.
|
||
|
||
## Naming and Fork Identity Policy
|
||
|
||
This plan assumes that the project is now **Fruix** as a user-facing system, in the same way that the store root has already moved from `/gnu` to `/frx`.
|
||
|
||
The naming policy for the next phases is:
|
||
|
||
- the user-facing CLI should become `fruix`
|
||
- user-facing daemon, service, help text, generated documentation, and system identity should prefer `fruix` over `guix`
|
||
- new fork-specific modules and code should prefer `fruix` namespaces and naming
|
||
- the `/frx` path family remains the canonical store/state/config root for FreeBSD work
|
||
- internal upstream-derived module namespaces should **not** be renamed mechanically just for branding
|
||
- names containing `gnu` should be preserved where they refer to actual GNU concepts or components, such as:
|
||
- GNU packages
|
||
- GNU Shepherd
|
||
- `gnu-build-system`
|
||
|
||
This means the early integration work will often need to distinguish between:
|
||
|
||
- the **current upstream-derived internal checkout reality**, where the failing command path is still `./pre-inst-env guix ...`, and
|
||
- the **desired Fruix product boundary**, where the operator-facing command should become `fruix`.
|
||
|
||
Accordingly, the next phases should first make the current checkout runnable, then establish a controlled rename boundary for the user-facing command and related interfaces, rather than attempting a blanket internal rename of every `guix` or `gnu` identifier.
|
||
|
||
---
|
||
|
||
## Current State at the End of Phase 4
|
||
|
||
The following have already been validated on the active FreeBSD amd64 host:
|
||
|
||
- fixed local Guile path suitable for subprocess-heavy Scheme work
|
||
- FreeBSD jail-first build isolation prototype
|
||
- privilege drop and build-user isolation prototype
|
||
- `/frx/store` prototype with GC-style rooted retention
|
||
- reusable FreeBSD GNU build-system adaptation across representative packages
|
||
- prototype FreeBSD package-definition/profile layer
|
||
- Shepherd build and runtime supervision on FreeBSD
|
||
- Shepherd `rc.d` integration prototype
|
||
- Shepherd bridge layer for FreeBSD rc.d, loopback/network, tmpfs, and user/group administration
|
||
|
||
The largest remaining blocker before real system generation is still the live upstream-derived checkout/runtime path, especially:
|
||
|
||
- `./pre-inst-env guix --version`
|
||
- `Wrong type to apply: #<syntax-transformer leave-on-EPIPE>`
|
||
|
||
That blocker is the first item in the next phase because without it, system composition cannot honestly move from prototype scaffolding to real Fruix/Guix execution. Resolving it is also the prerequisite for introducing a trustworthy `fruix` user-facing command boundary on top of the current checkout.
|
||
|
||
---
|
||
|
||
## Phase 5: Real Guix Checkout and Host Runtime Unblocking on FreeBSD
|
||
|
||
### Intermediate Goal 5.1: Unblock the Uninstalled Checkout Command Path and Establish the `fruix` CLI Boundary
|
||
|
||
The immediate goal is to move from “the checkout configures and builds some generated scripts” to “the checkout actually runs on FreeBSD”.
|
||
|
||
Because the current checkout still exposes an upstream-derived `guix` command path, this step must also define how the user-facing `fruix` command is introduced without forcing a premature blanket rename of internal identifiers.
|
||
|
||
**Verification Goal 5.1:** Investigate and fix the runtime failure in the uninstalled checkout command path:
|
||
|
||
- `./pre-inst-env guix --version`
|
||
- `./pre-inst-env guix repl`
|
||
- `./pre-inst-env guix build --help`
|
||
|
||
Then, once that path works, introduce a first user-facing `fruix` command boundary for the checkout, whether by renamed frontend, generated wrapper, compatibility symlink policy, or another documented transition mechanism.
|
||
|
||
The investigation must identify whether the `leave-on-EPIPE` failure is due to:
|
||
|
||
- macro expansion / module ordering assumptions,
|
||
- Guile version or module interaction,
|
||
- FreeBSD-specific POSIX behavior,
|
||
- generated source ordering,
|
||
- or a latent upstream bug exposed by FreeBSD.
|
||
|
||
Produce:
|
||
|
||
- a dedicated reproducer harness,
|
||
- a root-cause report,
|
||
- and either:
|
||
- an in-repo patch queue for the Guix checkout, or
|
||
- a precise upstreamable fix series.
|
||
|
||
**Success Criteria:** `./pre-inst-env guix --version` succeeds on FreeBSD using the fixed local Guile stack, at least one additional non-trivial checkout subcommand works, and there is a documented first-step user-facing `fruix` frontend policy for the live checkout.
|
||
|
||
### Intermediate Goal 5.2: Validate Package Lowering and Derivation Generation
|
||
|
||
Once the checkout is runnable, the next boundary is host-side lowering rather than builder-side phase execution.
|
||
|
||
**Verification Goal 5.2:** Demonstrate that a real Guix checkout on FreeBSD can lower at least one tiny package to a derivation using the `/frx` store settings. This step should verify:
|
||
|
||
- package -> bag lowering,
|
||
- bag -> derivation lowering,
|
||
- imported module closure materialization,
|
||
- fixed-output source origin handling,
|
||
- and generation of a real derivation file targeting `/frx/store`.
|
||
|
||
The initial validation target should be intentionally small, such as a previously successful package from the build-system work (`hello`, `which`, or similar).
|
||
|
||
**Success Criteria:** a real derivation is emitted by Guix on FreeBSD, references `/frx/store`, and can be inspected as a normal derivation rather than only as a prototype metadata file.
|
||
|
||
### Intermediate Goal 5.3: Establish Store Connection and Minimal Daemon RPC Integration
|
||
|
||
At this point, the remaining gap is the connection between host-side lowering and actual store-backed builds.
|
||
|
||
**Verification Goal 5.3:** Connect the runnable checkout to a minimal FreeBSD-aware store/daemon path. This should include:
|
||
|
||
- confirming `open-connection` / store socket behavior on FreeBSD,
|
||
- minimal daemon configuration for `/frx/store`,
|
||
- validating store registration and metadata behavior,
|
||
- and ensuring that at least one real derivation can be submitted to the daemon.
|
||
|
||
This phase may still rely on prototype jail/build-user implementations while integrating them incrementally into daemon behavior.
|
||
|
||
**Success Criteria:** a real checkout command path can contact a FreeBSD-aware daemon/store path and submit at least one derivation-backed build request, with the intended user-facing interface moving toward `fruix`.
|
||
|
||
---
|
||
|
||
## Phase 6: Real FreeBSD-Backed Store Builds Through Guix/Fruix Mechanisms
|
||
|
||
### Intermediate Goal 6.1: Build a Real Package Into `/frx/store`
|
||
|
||
The builder-side phase runners proved that GNU packages can build on FreeBSD. This phase requires proving that they can do so through real lowering + store + daemon plumbing.
|
||
|
||
**Verification Goal 6.1:** Build at least one small GNU package from a real Guix package definition all the way into `/frx/store`, through the daemon path. Validate:
|
||
|
||
- store path creation,
|
||
- output registration,
|
||
- runtime execution from the store,
|
||
- and preservation of declared references.
|
||
|
||
**Success Criteria:** `fruix build`-style execution succeeds for at least one real package on FreeBSD and produces a usable output in `/frx/store`, even if the underlying checkout still retains transitional upstream-derived command plumbing.
|
||
|
||
### Intermediate Goal 6.2: Integrate FreeBSD Jail/Privilege Prototypes Into the Real Build Path
|
||
|
||
The current jail/build-user work is still prototype-scoped. It now needs to become part of the actual FreeBSD build path.
|
||
|
||
**Verification Goal 6.2:** Adapt the daemon build path so that real derivation builds use the already validated FreeBSD primitives:
|
||
|
||
- thin jails,
|
||
- explicit `nullfs` mount plans,
|
||
- dropped build users,
|
||
- `/frx/store` read semantics,
|
||
- and per-build writable scratch/output directories.
|
||
|
||
At least one real package build should be observed executing inside that integrated jail/build-user path.
|
||
|
||
**Success Criteria:** a real Guix/Fruix build runs inside a FreeBSD jail with dropped build credentials and succeeds into `/frx/store`.
|
||
|
||
### Intermediate Goal 6.3: Validate Profiles and User-Facing Package Installation
|
||
|
||
Once real builds land in the store, the next user-facing layer is profile generation.
|
||
|
||
**Verification Goal 6.3:** Demonstrate that a user profile can be produced from real store items on FreeBSD, not only from the earlier package/profile prototype layer. Validate:
|
||
|
||
- profile symlink generation,
|
||
- transaction semantics at least in a minimal form,
|
||
- PATH usability,
|
||
- and user access to built packages.
|
||
|
||
**Success Criteria:** a package built through the real FreeBSD store path can be installed into a user profile and executed through that profile.
|
||
|
||
---
|
||
|
||
## Phase 7: Declarative Fruix System Composition on FreeBSD
|
||
|
||
### Intermediate Goal 7.1: Define a Minimal Fruix Operating-System Model
|
||
|
||
The package layer alone is not enough; there must be a system-level object that describes an installable/bootable Fruix system.
|
||
|
||
**Verification Goal 7.1:** Define a minimal Fruix system model for FreeBSD, analogous in spirit to Guix System’s operating-system record, that can describe:
|
||
|
||
- kernel and boot assets,
|
||
- essential FreeBSD base packages,
|
||
- `/etc` configuration files,
|
||
- activation requirements,
|
||
- Shepherd configuration,
|
||
- file systems,
|
||
- users/groups,
|
||
- and host identity settings.
|
||
|
||
This may begin in Fruix-specific modules rather than attempting immediate full upstream Guix OS integration.
|
||
|
||
**Success Criteria:** a declarative system description can be evaluated into a coherent system closure specification rather than only a loose collection of packages.
|
||
|
||
### Intermediate Goal 7.2: Generate a Minimal System Closure and Activation Payload
|
||
|
||
The next step is to turn the system model into concrete filesystem content.
|
||
|
||
**Verification Goal 7.2:** Generate a minimal system closure for FreeBSD that includes at least:
|
||
|
||
- FreeBSD kernel and boot files,
|
||
- minimal userland,
|
||
- required `/etc` files,
|
||
- Shepherd service definitions and startup hook,
|
||
- activation scripts,
|
||
- and references into `/frx/store`.
|
||
|
||
This phase should decide and document whether the first boot target uses:
|
||
|
||
- FreeBSD init + `rc.d` to launch Shepherd, or
|
||
- Shepherd as PID 1.
|
||
|
||
For the first integrated boot target, FreeBSD init + `rc.d` is acceptable if it is generated declaratively from the system definition.
|
||
|
||
**Success Criteria:** a system closure directory tree can be generated reproducibly from a declarative Fruix system description.
|
||
|
||
### Intermediate Goal 7.3: Validate an Installable Root Filesystem Tree
|
||
|
||
Before generating a disk image, validate that the system closure can be materialized as a rootfs tree suitable for installation or image creation.
|
||
|
||
**Verification Goal 7.3:** Build a root filesystem tree that contains:
|
||
|
||
- bootable kernel/loader content,
|
||
- generated configuration,
|
||
- declared users/groups,
|
||
- Shepherd launch integration,
|
||
- and enough runtime content to reach a login prompt or equivalent ready state.
|
||
|
||
**Success Criteria:** the generated rootfs is internally coherent and passes static validation for image construction.
|
||
|
||
---
|
||
|
||
## Phase 8: FreeBSD/Fruix Image Construction for bhyve
|
||
|
||
### Intermediate Goal 8.1: Create a Reproducible Disk Image Build Path
|
||
|
||
Now that the system closure exists, produce a real boot medium from it.
|
||
|
||
**Verification Goal 8.1:** Create tooling to generate a bootable bhyve-compatible disk image from the Fruix system closure. This should include:
|
||
|
||
- partition map creation,
|
||
- filesystem creation,
|
||
- rootfs population,
|
||
- boot asset installation,
|
||
- and serial-console-friendly configuration.
|
||
|
||
The first target should prefer the simplest debuggable bhyve path, likely:
|
||
|
||
- raw disk image,
|
||
- UEFI boot,
|
||
- FreeBSD kernel/loader,
|
||
- serial console.
|
||
|
||
**Success Criteria:** a reproducible image file is generated from the declarative Fruix system description and passes boot-structure sanity checks.
|
||
|
||
### Intermediate Goal 8.2: Integrate Image Generation With the Fruix System Definition Layer
|
||
|
||
The image builder should not remain a detached shell-only utility.
|
||
|
||
**Verification Goal 8.2:** Connect image generation to the system description layer so that the image is the output of system composition, not a manual afterthought. Ideally, this begins to resemble a minimal FreeBSD analogue of `fruix system image`, even if some internal machinery still derives from upstream Guix code paths.
|
||
|
||
**Success Criteria:** a single declarative Fruix system description can drive image generation end-to-end.
|
||
|
||
---
|
||
|
||
## Phase 9: Boot a Minimal Fruix System in bhyve
|
||
|
||
### Intermediate Goal 9.1: Reach a Serial-Console Boot Milestone
|
||
|
||
The first VM target should be intentionally narrow: boot the generated image and prove it reaches a known-good ready state.
|
||
|
||
**Verification Goal 9.1:** Create a bhyve launcher/validation harness that:
|
||
|
||
- boots the generated image,
|
||
- captures serial output,
|
||
- waits for a ready marker,
|
||
- and tears down the VM cleanly.
|
||
|
||
The ready marker may initially be one of:
|
||
|
||
- a login prompt,
|
||
- a Shepherd status marker,
|
||
- or a deterministic boot-complete message.
|
||
|
||
**Success Criteria:** the Fruix image boots in bhyve and reaches a deterministic serial-console ready state.
|
||
|
||
### Intermediate Goal 9.2: Validate In-Guest Shepherd and Core Services
|
||
|
||
After basic boot, verify that the system behaves like a Fruix system and not merely a FreeBSD guest with some files copied in.
|
||
|
||
**Verification Goal 9.2:** Validate in the running bhyve guest that:
|
||
|
||
- Shepherd is active,
|
||
- the expected essential services are started,
|
||
- the generated configuration is in effect,
|
||
- and the system closure in the guest matches the declared build outputs.
|
||
|
||
**Success Criteria:** the bhyve guest boots the generated Fruix system and runs Shepherd-managed core services successfully.
|
||
|
||
### Intermediate Goal 9.3: Validate Minimal Operator Usability
|
||
|
||
The final milestone for the first bhyve system is not completeness, but minimal usefulness.
|
||
|
||
**Verification Goal 9.3:** Confirm that the guest supports a minimal operator workflow, such as:
|
||
|
||
- console login or root shell access,
|
||
- inspection of Shepherd service status,
|
||
- reading generated configuration files,
|
||
- and optionally basic networking.
|
||
|
||
**Success Criteria:** a human operator can boot the bhyve guest and verify a minimally usable Fruix environment from inside the VM.
|
||
|
||
---
|
||
|
||
## Optional Phase 10: Replace Transitional Pieces With More Native Fruix/Guix Machinery
|
||
|
||
This phase is intentionally deferred until after the first real bhyve success.
|
||
|
||
Possible follow-up work includes:
|
||
|
||
- replacing residual prototype-only composition layers with true Guix-native system-generation mechanisms,
|
||
- deeper integration of FreeBSD package definitions with real package records and lowering paths,
|
||
- improving daemon/store metadata fidelity,
|
||
- turning image generation into a true `fruix` command,
|
||
- and evaluating whether Shepherd-as-PID-1 is worth pursuing beyond the validated `rc.d` bridge path.
|
||
|
||
---
|
||
|
||
## Recommended Immediate Order of Work
|
||
|
||
If work begins on this plan now, the recommended near-term order is:
|
||
|
||
1. **Phase 5.1** — fix the `leave-on-EPIPE` checkout/runtime blocker
|
||
2. **Phase 5.2** — prove real derivation generation on FreeBSD with `/frx/store`
|
||
3. **Phase 5.3 / 6.1** — connect real builds to the daemon/store path and land one package in `/frx/store`
|
||
4. **Phase 6.2** — attach the real build path to FreeBSD jail/build-user isolation
|
||
5. **Phase 7** — define the minimal declarative Fruix system model and rootfs closure
|
||
6. **Phase 8–9** — generate and boot the first bhyve image
|
||
|
||
This ordering keeps the project honest: the bhyve image milestone will be the output of genuine Guix/Fruix integration work rather than a prototype assembled around unresolved core runtime issues. It also keeps the naming transition disciplined: user-facing `fruix` surfaces can be introduced deliberately on top of a working checkout instead of via a destabilizing whole-tree rename.
|
||
|
||
---
|
||
|
||
## Final Target of This Plan
|
||
|
||
The end goal of this Path B plan is:
|
||
|
||
> A minimal Fruix system for FreeBSD, generated through real Guix/Fruix-style mechanisms, exposed to operators through `fruix` user-facing interfaces, stored under `/frx`, bootable in bhyve, and capable of running Shepherd-managed core services.
|
||
|
||
This target explicitly assumes selective renaming rather than blanket renaming: Fruix at the product boundary, stable upstream-derived internal names where they continue to provide architectural or maintenance value, and preserved GNU naming where it refers to genuine GNU components.
|
||
|
||
That is the next major milestone after the completed foundational and prototype phases.
|