Files

530 lines
15 KiB
Markdown

# Fruix plan and package model notes
This document captures the current Fruix direction for package identity, base
artifacts, ports integration, installer/product milestones, and a few open
issues discovered during current work.
## Current package model
Fruix currently has three practical lanes.
### 1. FreeBSD base artifacts
These are the packages that represent FreeBSD base/system pieces:
- kernel
- world
- runtime slice
- bootloader slice
- headers / SDK slice
Today there are two forms.
#### A. Host-staged bootstrap/prototype packages
Examples:
- `freebsd-kernel`
- `freebsd-userland`
- `freebsd-runtime`
- `freebsd-bootloader`
- `freebsd-rc-scripts`
- `freebsd-networking`
- `freebsd-openssh`
These use `copy-build-system` and stage files from the current host system.
They are useful for bootstrap, recovery, and validating the Fruix store/system
model before all artifacts are built natively by Fruix.
#### B. Native Fruix-built base artifacts
Examples:
- `freebsd-native-kernel`
- `freebsd-native-world`
- `freebsd-native-runtime`
- `freebsd-native-bootloader`
- `freebsd-native-headers`
These use `freebsd-kernel-build-system` or `freebsd-world-build-system` and are
built from a declared `freebsd-base`, which itself points at a declared
`freebsd-source`.
This is the real long-term direction.
### 2. FreeBSD ports-style packages
Examples today:
- `freebsd-gmake`
- `freebsd-bash`
- `freebsd-autotools`
- `freebsd-openssl`
- `freebsd-zlib`
- `freebsd-nodejs`
- `freebsd-npm`
- `freebsd-ripgrep`
- `freebsd-tmux`
- `freebsd-neovim`
These are currently hand-written host-staged packages that mostly repackage
host-installed files from `/usr/local` into Fruix store objects.
They are useful as a bootstrap path, but they are still host-dependent and can
fail when the builder host does not have the exact expected files installed.
### 3. Other Fruix packages
Long term, packages that are not part of FreeBSD base or directly mirrored from
Ports should just become normal Fruix packages.
This should not be treated as a special "Guix compatibility" category forever.
The likely end state is simply:
- Fruix has its own package model
- packages may be inspired by, imported from, or rewritten from Guix packages
- but they become normal Fruix packages with Fruix-native metadata and build
policy
## Naming direction
Current names reflect history:
- `freebsd-*` usually means host-staged/bootstrap/prototype packages
- `freebsd-native-*` means Fruix-native built base artifacts
That naming is likely backwards for the long term.
### Proposed future rename
When the native path is complete enough to become the canonical path:
- rename `freebsd-native-*` -> `freebsd-*`
- rename current host-staged/bootstrap packages to something like:
- `freebsd-bootstrap-*`
- or `freebsd-host-staged-*`
Recommendation:
- do **not** rename immediately while both paths are still actively in use
- first finish the native base path and make it the default serious workflow
- then do a compatibility transition with aliases/deprecation
That keeps user-facing names aligned with the intended product identity:
- `freebsd-*` should mean the real Fruix-managed thing
- bootstrap/prototype packages should say so explicitly
## Replacement plan already visible in the code
The current host-staged replacement order already suggests the intended
migration:
1. first-wave
- `freebsd-kernel`
- `freebsd-bootloader`
2. second-wave
- `freebsd-runtime`
- `freebsd-libc`
- `freebsd-userland`
- `freebsd-rc-scripts`
3. third-wave
- `freebsd-networking`
- `freebsd-openssh`
4. fourth-wave
- `freebsd-kernel-headers`
- `freebsd-clang-toolchain`
5. fifth-wave
- development/user packages like:
- `freebsd-gmake`
- `freebsd-autotools`
- `freebsd-openssl`
- `freebsd-zlib`
- `freebsd-nodejs`
- `freebsd-ripgrep`
- `freebsd-neovim`
- etc.
The broad strategy should remain:
- bootstrap with host-staged packages
- move base/system pieces to native Fruix builds from declared source
- then replace brittle host-staged development/profile/package layers
## FreeBSD version/base story
Fruix already has the core abstractions needed to build arbitrary declared
FreeBSD bases:
- `freebsd-source`
- `freebsd-base`
- `freebsd-native-kernel-for`
- `freebsd-native-world-for`
- `freebsd-native-runtime-for`
- `freebsd-native-bootloader-for`
- `freebsd-native-headers-for`
That means Fruix can already express things like:
- FreeBSD 14.2 source
- a 14.2 base definition
- a 14.2 runtime/world/kernel artifact build
What is still missing is a polished user-facing workflow for this.
### Desired user-facing capability
Eventually Fruix should make this easy:
- build FreeBSD 14.2 runtime/world/kernel as first-class store objects
- assemble a runnable environment from those artifacts
- run tools like `ls` from that declared base/runtime cleanly
Today this is possible in principle through Scheme-level composition, but not
as a polished one-line CLI workflow.
## Ports strategy
A pragmatic path for Ports support is probably best.
### Phase 1: make them work first
Start with a simple path that fits Fruix reasonably well and actually works:
- repackage installed host Ports or pkg artifacts into Fruix store objects
- record provenance and host/source assumptions clearly
- keep them usable inside Fruix profiles and system closures
This is not perfect or fully reproducible, but it is practical.
### Phase 2: auto-package installed/pkg artifacts
Add a more automated import story for Ports-derived software, for example by:
- reading installed package metadata
- reading package file lists/manifests
- generating Fruix package definitions or manifests automatically
This would automate the current hand-written host-staged ports package pattern.
### Phase 3: source-built Ports under Fruix control
Longer term:
- build Ports in jails under Fruix executor policy
- import resulting artifacts into the Fruix store
- make them first-class Fruix package identities
This is a bigger project and should likely wait until the native base and
closure/image stories are more mature.
## Native build identity and executor model
These remain core product ideas.
### Native base builds should be first-class Fruix objects
Native build results should feel like real immutable Fruix identities:
- world
- kernel
- bootloader
- headers / SDK
Not just files in a temporary result area.
Desired model:
- staging/result roots are temporary build/result areas
- `/frx/store/...` remains the real immutable identity
- promoted native artifacts record:
- executor
- source identity
- provenance
- build policy
Status: **mostly implemented / in progress**.
Fruix already has native build result promotion and store objects, but the full
user-facing workflow still needs more polish.
### Executor model
The intended executor abstraction should cover the same declared build with
multiple execution backends:
- `host`
- `jail`
- `ssh-guest`
- `self-hosted`
- later maybe `remote-builder`
Desired mental model:
- same declared source
- same expected outputs
- same provenance shape
- executor is a policy choice, not an architecture fork
Status: **substantially in progress**.
We already have a real executor model and jail-backed execution is becoming a
first-class local primitive.
### Promotion step
Desired lifecycle:
1. build runs somewhere
2. result lands in a staging/result root
3. Fruix validates result shape and metadata
4. Fruix promotes successful results into store objects
5. system declarations consume promoted artifacts
Status: **mostly implemented / in progress**.
## Jails as the normal local isolation primitive
A strong Fruix-on-FreeBSD direction is to treat jails as the normal local build
and assembly isolation primitive.
Current progress:
- copy builds can run in jails
- native build commands can run in jails
- package profile materialization uses jailed merge policy
- system closure profile/development-profile/build-profile assembly uses jailed
union-tree policy
- rootfs population helpers now run through the jailed helper path too
- image/installer/ISO assembly now records staged jail metadata and uses a
dedicated assembly privileged policy surface
- install-time storage layout application is now executed as a rendered
privileged helper script under that policy instead of many scattered host-side
privileged calls
- remaining image resize and raw-file install attach/detach paths now also run
through dedicated assembly privileged helper scripts, further shrinking the
host-side privileged call surface
- network is disabled by default in these jailed paths
- union assembly mounts are now much smaller and omit host `/etc` and `devfs`
- direct block-device `system install` is now an explicit opt-in under the
assembly policy instead of the default path
Next likely steps:
- keep pushing filesystem/image construction toward file-backed or jailed helper
paths where practical
- decide whether any remaining assembly-time host assumptions should move behind
an even narrower dedicated helper or runner boundary
## Runtime / development / build separation
Fruix should keep three layers conceptually distinct:
- runtime profile
- development profile
- build profile / build environment
The build layer should be:
- more sanitized
- more reproducible
- less interactive
- closer to the exact contract needed for buildworld/buildkernel and other
reproducible builds
Status: **partially present**, but likely still needs clearer user-facing and
internal modeling.
## Installed systems as real Fruix nodes
Installed systems should increasingly feel like managed Fruix machines rather
than deployed images with helper scripts.
Desired node-local lifecycle:
- `fruix system build`
- `fruix system reconfigure`
- `fruix system upgrade`
- `fruix system build-base`
- `fruix system deploy`
- `status`
- `switch`
- `rollback`
Key requirement:
- no hidden dependence on a bootstrap checkout
- no hidden dependence on ambient host paths
- no hidden dependence on ad hoc bootstrap-only mechanics
Status: **partial / in progress**.
See also: `docs/plan_2.md` for the follow-on lifecycle plan focused on
node-local management, deploy hardening, and the later source/pin/upgrade path.
## Source / pin / update / lock workflow
Source provenance should not only be recorded after the fact; it should become
part of the operator workflow.
Desired lifecycle:
- source object
- pin/lock/update decision
- build result
- promoted base artifacts
- deployed system generation
This is very Fruix, but it does not have to be the first user-facing milestone.
Status: **mostly open**.
## Publication / substitutes / build once deploy many
Before scaling self-hosting too far, Fruix should have at least a minimal story
for:
- store export/import
- artifact publication
- fetch/substitute-like reuse
- trust/provenance verification
Goal:
- build once
- publish/store/export once
- deploy many
Status: **open**.
## Product/operator workflow
A good small canonical Fruix lifecycle is still a useful north star:
1. update/pin source inputs
2. build base artifacts
3. build system closure
4. publish/promote results
5. deploy/switch
6. rollback if needed
Fruix feels most like Fruix when the operator thinks in terms of:
- declared sources
- promoted base artifacts
- immutable system generations
- executor policy
and not in terms of raw `make buildworld` mechanics.
## fruix / fruix-bootstrap boundary
The contract should be:
### fruix-bootstrap
- foreign FreeBSD bring-up
- installer media
- host-side bootstrap/install/deploy helpers
- enough machinery to seed the first Fruix node onto disk
### fruix
- package definitions
- system definitions
- node CLI
- build/reconfigure/build-base/deploy logic
- future source/update/lock/publish/substitute logic
The installer should install a pinned Fruix revision, and after first boot the
node should move forward using Fruix itself, not the bootstrap repo.
Status: **needs to be made explicit**.
## Installer and pinned Fruix workflow
A near-term product milestone should remain:
- install a pinned Fruix onto a machine through a small TUI installer
- then have the booted node depend only on Fruix from there onward
Suggested phases:
### Phase A — establish source of truth
- define `fruix` vs `fruix-bootstrap`
- make installed systems point at pinned Fruix
### Phase B — guided installation
- TUI installer on the current installer ISO
- generate declaration + pin + install metadata
- boot into a usable Fruix node
### Phase C — operational usability
- ensure node-local lifecycle is complete enough
- add basic export/import or publish/fetch for artifacts
### Phase D — Fruix-native lifecycle
- source update/lock/compare
- later upgrade policy
- later substitutes/cache/trust refinement
- later deeper jail executor refinement
## Current open issues and recent lessons
### 1. Host-staged inputs are still brittle
Recent examples:
- unreadable host file inside `/etc/defaults` (`devfs.rules`) broke system build
hashing until Fruix stopped staging the entire directory
- `freebsd-autotools` currently assumes host paths such as
`/usr/local/share/autoconf2.72` exist
This reinforces the plan:
- host-staged packages are bootstrap tools
- they should not remain the canonical product identity forever
### 2. System build validation is still exposing host-assumption bugs
Recent validation has moved past one host-input bug only to reveal others.
This is useful and should continue:
- fix host-assumption bugs while bootstrap packages still exist
- keep pushing toward native-built base artifacts and less host dependence
### 3. Union-tree mount minimization is the right direction
Recent work reduced union/closure/profile assembly jail mounts to a much smaller
runtime surface.
That direction has now continued into:
- rootfs population helpers
- image/installer/ISO assembly under a dedicated privileged builder policy
The next refinement is to reduce the remaining privileged host-side surface
further, especially around final image/device manipulation.
## Summary
Short version:
- native base artifacts are the real Fruix direction
- current `freebsd-*` host-staged packages are bootstrap/prototype tools
- once the native path is complete enough, rename so the real Fruix-managed
artifacts get the canonical `freebsd-*` names
- Ports should probably start with a simple "works first" integration path
before aiming for full source-built ports under Fruix control
- packages imported or inspired from Guix should ultimately just become normal
Fruix packages
- the product center of gravity should remain:
- declared source identity
- promoted immutable artifacts
- FreeBSD-native execution/isolation
- small opinionated operator workflows