530 lines
15 KiB
Markdown
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
|