# 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