diff --git a/docs/review.md b/docs/review.md new file mode 100644 index 0000000..43cfc8c --- /dev/null +++ b/docs/review.md @@ -0,0 +1,599 @@ +# Fruix System Review + +An external review of the Fruix operating system: a hybrid combining +the FreeBSD kernel and userland with GNU Guix-style declarative package +management and GNU Shepherd as the init system. + +Review date: 2026-04-07 + +--- + +## 1. Philosophical Fit + +### Do FreeBSD and Guix Complement Each Other? + +Yes — and more naturally than one might expect. The two projects share +a deeper kinship than their surface differences suggest: + +- **Clean boundaries.** FreeBSD's base/ports split is structurally + analogous to Guix's separation of the system definition from the + package graph. Both projects value knowing exactly what "the system" + is versus what the user has layered on top. Fruix exploits this + directly: FreeBSD's base becomes the declared `freebsd-base` record, + and everything above it is Guix-managed store content. + +- **Whole-system coherence.** FreeBSD builds its base as a single + coordinated unit (`make world`). Guix builds its system as a single + coordinated closure. Fruix's three-layer profile model + (runtime/development/build) maps cleanly onto both traditions — + FreeBSD developers will recognise the split as familiar, and Guix + users will recognise the closure semantics. + +- **Conservative engineering.** Both upstream projects favour + correctness over novelty. FreeBSD's resistance to unnecessary + abstraction pairs well with Guix's emphasis on reproducibility over + convenience. The result is a system where "works the same way every + time" is a shared first principle, not a bolted-on afterthought. + +- **Auditability.** FreeBSD's single-source-tree base is already + unusually auditable for a Unix. Adding content-addressed store paths + and explicit provenance metadata makes it *more* auditable, not less. + The Fruix design of embedding `metadata/freebsd-source.scm` and + materialisation records into every closure is a genuine improvement + over either upstream's status quo. + +The combination is not merely compatible — it is arguably a better +expression of both projects' values than either achieves alone. FreeBSD +gains reproducibility and declarative configuration. Guix gains a +mature, coherent kernel and userland with superior network stack, jail +isolation, and ZFS. Neither project currently offers what the hybrid +provides. + +### Tension Points + +The tensions are real but tractable. Each one has already been +partially addressed in the current codebase: + +**Mutable `/etc` vs. store-based immutability.** FreeBSD expects +`/etc` to be a mutable directory owned by the administrator. Guix +expects it to be a rendered artifact derived from the system +declaration. Fruix currently renders `/etc` files at activation time +from the closure's metadata — this is the correct approach, but it +will need careful handling as the system grows. The risk is drift: if +an operator hand-edits `/etc/rc.conf` and the next `reconfigure` +silently overwrites it, trust erodes. The solution (used by NixOS) is +to make the rendered files clearly machine-managed and to provide a +sanctioned escape hatch for local overrides. Fruix should plan for +this now, before the habit of hand-editing `/etc` becomes entrenched +among early users. + +**FreeBSD's boot/rc model vs. Shepherd.** The dual-boot-mode design +(`freebsd-init+rc.d-shepherd` and `shepherd-pid1`) is a pragmatic +and intelligent decision. It allows incremental migration: operators +can start with FreeBSD init managing rc.d scripts (familiar) and +transition to Shepherd as PID 1 (more Guix-native) when confidence +grows. The tension here is that maintaining two boot paths doubles the +surface area for boot bugs. Over time, one mode should become the +default and the other should be explicitly marked as legacy. +Recommendation: `shepherd-pid1` should be the default for new +installations once it has been validated on a wider range of hardware. + +**The role of the base system.** In stock Guix, the kernel and core +userland are packages in the store like any other. In Fruix, the +FreeBSD base is a privileged entity: it is built via `make world` +rather than through the Guix build system, and it has its own +source-materialisation and promotion pipeline. This asymmetry is +necessary (FreeBSD's build system is not trivially expressible as a +Guix derivation), but it creates a conceptual gap. Users coming from +Guix will ask "why can't I just `guix package -i kernel`?" and users +coming from FreeBSD will ask "why is my kernel in `/frx/store`?" +Clear documentation explaining the boundary and the reasons for it +is essential. + +**rc.conf semantics.** The operating-system model includes +`rc-conf-entries`, which is a direct concession to FreeBSD's +configuration idiom. This is fine as a compatibility bridge, but it +introduces a second configuration surface alongside Shepherd service +declarations. Over time, the system should converge on one +authoritative service-configuration mechanism. The current design +wisely keeps rc.conf entries as a rendered output of the declaration +rather than a primary input. + +### What Does the Hybrid Offer? + +The value proposition, stated plainly: + +1. **For FreeBSD users:** Declarative, reproducible system + configuration with transactional upgrades and rollback — things + FreeBSD has never had. The ability to define an entire system in a + single Scheme file and materialise it deterministically is + transformative for FreeBSD operations. + +2. **For Guix/NixOS users:** FreeBSD's kernel, network stack, jails, + ZFS, DTrace, and Capsicum — mature subsystems that Linux either + lacks or implements differently. Plus a genuinely unified base + system rather than a collection of independently-packaged GNU + components. + +3. **For both:** A system where provenance is tracked from source + commit through build through deployment, with explicit generation + metadata and rollback. Neither upstream currently offers the full + chain that Fruix is building. + +The value proposition is clear. The question is not "is this worth +doing?" but "can a small team sustain the maintenance burden of a +hybrid?" That is an execution risk, not a design flaw. + +### Is Shepherd a Good Fit? + +Shepherd is a reasonable choice, with caveats: + +**Strengths:** Scheme-native service definitions compose naturally +with the Guix-style system declaration. Shepherd's service graph +model is more expressive than rc.d's linear ordering. The existing +FreeBSD-specific service helpers (`freebsd-rc-service`, +`freebsd-tmpfs-service`, etc.) demonstrate that Shepherd can drive +FreeBSD's native tools without friction. + +**Concerns:** FreeBSD's kernel hands off to `/sbin/init` with +specific expectations about early boot (single-user mode, fsck, +console configuration). Shepherd-as-PID-1 must handle these +correctly or the system will be fragile in recovery scenarios. +FreeBSD jails expect their own init process and service tree — jail +integration will need either per-jail Shepherd instances or a clean +delegation model. The current implementation does not yet address +jail service management, and this should be a design priority before +jails are documented as a supported feature. + +The rc.d bridge mode provides a safe fallback, which is wise. But +the long-term health of the project depends on Shepherd-as-PID-1 +being robust enough that operators trust it for production FreeBSD +workloads. This requires focused testing of edge cases: kernel panic +recovery, single-user boot, fsck failures, and unclean shutdown +handling. + +--- + +## 2. Documentation Strategy + +### Documentation Layers Needed + +The system requires five distinct documentation layers, in priority +order: + +**Layer 1: Conceptual Guide ("What is Fruix?")** +Priority: Critical. This is the first thing any visitor encounters. +It must explain: +- What the system is and why it exists +- The FreeBSD base + Guix store + Shepherd init architecture +- How it differs from stock FreeBSD and stock Guix +- The mental model: declarations, closures, generations, store paths +- Who this is for and what the tradeoffs are + +This cannot be derived from either upstream's documentation. It must +be written from scratch. Target length: 10-15 pages. This is the +single highest-priority documentation deliverable. + +**Layer 2: Quick-Start / Installation Guide** +Priority: Critical. A new user must be able to: +- Obtain or build an installer image +- Install Fruix on a machine (real or virtual) +- Write a minimal system declaration +- Build and deploy it +- Understand what happened and what to do next + +This is partially derivable from the existing phase reports but needs +to be written as a clean, linear narrative. The bootstrap procedure +(`prepare-builder`) should be documented separately as a contributor +guide, not as the primary installation path. + +**Layer 3: System Declaration Reference** +Priority: High. A complete reference for the `operating-system` +record and all its constituent types: +- `freebsd-base`, `freebsd-source` +- Package lists and profiles +- `storage-layout`, partitions, filesystems +- Users, groups, services +- Shepherd service declarations +- Init modes, loader entries, rc.conf entries + +This is the equivalent of the NixOS options reference or the Guix +system configuration reference. It should be generated or +semi-generated from the Scheme record definitions where possible. + +**Layer 4: Command Reference** +Priority: High. Every `fruix` subcommand documented: +- `fruix system build|image|install|deploy|reconfigure|switch|rollback` +- `fruix source materialize` +- `fruix native-build promote` +- Node-local commands (`fruix system status`, `fruix system build-base`) + +Format: man pages (see tooling recommendation below). + +**Layer 5: FreeBSD Integration Guide** +Priority: Medium. For users with FreeBSD background: +- What changed from stock FreeBSD and why +- Where familiar tools still work (`ifconfig`, `gpart`, `service`) +- Where they don't (hand-editing `/etc` files) +- How jails, ZFS, DTrace, and other FreeBSD features interact with + the Guix layer +- Migration guide from stock FreeBSD + +### Reconciling Upstream Documentation + +**FreeBSD man pages:** The stock FreeBSD man pages for kernel +interfaces, system calls, library functions, and file formats +(sections 2, 3, 4, 5, 7, 9) can be shipped largely unchanged. Section +1 (user commands) and section 8 (system administration) need auditing: +commands that Fruix overrides or wraps should get Fruix-specific man +pages. Section 5 pages for files that Fruix renders (e.g., `fstab(5)`, +`rc.conf(5)`, `loader.conf(5)`) should include a note that these are +managed by the system declaration and should not be hand-edited. + +**Guix manual:** The Guix manual's conceptual sections on the store, +derivations, profiles, and generations are excellent educational +material but describe GNU/Linux-specific implementation details. The +concepts can be adapted; the specifics must be rewritten. The Guix +system configuration reference is a good structural model but every +concrete example needs FreeBSD equivalents. The package management +sections are largely inapplicable until Fruix has its own package +management story beyond the base system. + +**What requires full rewrites:** +- Boot process documentation (entirely different from both upstreams) +- Service management (Shepherd on FreeBSD is unique to Fruix) +- System installation (Fruix's installer is its own thing) +- Package definitions (FreeBSD native builds differ from Guix derivations) +- Store layout (`/frx/store` vs. `/gnu/store`, different hash scheme) + +**What can be adapted with light editing:** +- FreeBSD kernel and hardware documentation +- Networking fundamentals (FreeBSD's stack is unchanged) +- Filesystem semantics (UFS/ZFS behaviour is unchanged) +- Guix conceptual material on functional package management + +### Highest-Priority Gaps + +**For FreeBSD users arriving:** +1. "Where is `rc.conf`?" — How system configuration works now +2. "How do I install software?" — The package/profile model +3. "Where is `/usr/local`?" — The store and profiles explained +4. "How do I update the system?" — Generations and reconfigure + +**For Guix/NixOS users arriving:** +1. "Where is `guix system reconfigure`?" — The Fruix equivalents +2. "Why is the kernel special?" — The FreeBSD base boundary +3. "Where are the packages?" — The current ecosystem scope +4. "How do services work?" — Shepherd on FreeBSD specifics + +### Tooling Recommendation + +**Man pages** for command references and file format references. Use +`mdoc(7)` format (FreeBSD's native macro set). Reasons: +- Offline availability without extra tooling +- Consistent with the FreeBSD base tradition +- Discoverable via `apropos(1)` +- Well-understood by the target audience + +**A static-site handbook** for conceptual guides, the quick-start, +and the system declaration reference. Recommended format: **AsciiDoc** +processed with Asciidoctor. Reasons: +- Richer structure than Markdown (admonitions, cross-references, + includes, conditional content) +- Generates HTML for a project website and PDF for offline use +- Lower barrier to contribution than Texinfo +- Used successfully by FreeBSD's own handbook toolchain + (though they use DocBook/AsciiDoc) + +**Not Texinfo.** While Guix uses Texinfo and it has strengths (Info +reader integration, structured cross-referencing), it is an +unfamiliar format for FreeBSD contributors and adds friction for a +project that needs to attract contributors from both communities. The +Guix community knows Texinfo; the FreeBSD community does not. AsciiDoc +is a pragmatic middle ground. + +### Man Pages vs. Separate Handbook + +Both. They serve different purposes: + +- **Man pages** for "how do I use this command right now?" — terse, + reference-oriented, available offline via `man(1)`. +- **Handbook** for "how does this system work and how do I configure + it?" — narrative, conceptual, cross-referenced. + +Consider a new man section (e.g., section `fruix` or a subsection +of section 8) for Fruix-specific system administration. This would +include: +- `fruix(8)` — overview and subcommand index +- `fruix-system(8)` — system management commands +- `fruix-deploy(8)` — deployment workflow +- `shepherd-freebsd(8)` — Shepherd integration specifics +- `operating-system.scm(5)` — system declaration file format + +--- + +## 3. Completeness Assessment + +### Installer / System Installation + +**Current state:** A working non-interactive installer (`fruix system +install`) and a prototype Newt TUI. Bootable installer images and +UEFI ISOs can be produced. Storage layout supports GPT + EFI + UFS + +optional swap. + +**Gaps:** +- ZFS root support (high demand from FreeBSD users, model prepared + but not implemented) +- Disk encryption (GELI/LUKS equivalent) +- Network-based installation (PXE, remote image fetch) +- Post-install first-boot wizard + +**Blocking?** No — the current installer is functional for basic use. +**Upstream reuse?** FreeBSD's `bsdinstall` is not easily adapted +(tightly coupled to rc.conf-based configuration). The Guix installer +is structurally similar but Linux-specific. The current from-scratch +approach is correct. +**Priority:** Medium. The installer works. ZFS support should be high +priority given the FreeBSD audience. + +### Bootstrap and Cross-Compilation + +**Current state:** A `prepare-builder` script that builds a local +Guile, Shepherd, and Guix checkout on a vanilla FreeBSD host. +Produces a reusable builder at `~/.local/opt/fruix-builder`. No +cross-compilation support. + +**Gaps:** +- No automated builder provisioning (e.g., a single `curl | sh` + bootstrap like Nix or Guix) +- No cross-compilation from non-FreeBSD hosts +- Builder preparation is slow and requires manual steps +- Guile subprocess stability workarounds suggest fragility + +**Blocking?** Partially — the current bootstrap works but is brittle +and underdocumented. A new contributor cannot easily get started. +**Upstream reuse?** Guix's bootstrap story is heavily Linux-specific. +FreeBSD's `pkg bootstrap` is irrelevant. This must be solved +independently. +**Priority:** High. The bootstrap experience is the first thing any +contributor or tester encounters. + +### Init and Service Management Completeness + +**Current state:** Shepherd runs as PID 1 or as an rc.d-launched +service. A handful of FreeBSD-specific service helpers exist +(loopback, tmpfs, user/group, rc-service wrapper). Basic services +(SSH, logging) are configured. + +**Gaps:** +- No Shepherd services for: cron, syslog (native), NTP, firewall + (pf/ipfw), devd, automount, powerd, jail management +- No service dependency graph for full FreeBSD boot sequence +- No `herd` equivalent for interactive service management + documentation +- No watchdog or health-check integration +- Jail service isolation not designed + +**Blocking?** Yes, for production use. The current set is sufficient +for a demo VM but not for a real workload. +**Upstream reuse?** Guix's service definitions are a structural model +but every concrete service must be rewritten for FreeBSD. FreeBSD's +rc.d scripts can be wrapped via `freebsd-rc-service` as a bridge. +**Priority:** High. Service completeness directly determines whether +the system is usable for real tasks. + +### System Configuration Interface + +**Current state:** System declarations are Scheme files loaded by +the `fruix` CLI. There is no interactive configuration tool, no +`guix system reconfigure` equivalent that reads from a canonical +system path, and no `guix system vm` for testing. + +**Gaps:** +- No canonical system declaration location (e.g., + `/etc/fruix/system.scm`) +- No `fruix system reconfigure` that reads from a default location + (the node-local version exists but requires the declaration to be + embedded in the closure) +- No `fruix system vm` for testing declarations without deploying +- No configuration validation beyond storage layout checks +- No configuration diffing ("what would change if I reconfigure?") + +**Blocking?** Partially. The current workflow (explicit paths) works +but is not ergonomic for daily use. +**Upstream reuse?** Guix's `guix system` subcommands are the direct +model. NixOS's `nixos-rebuild` is another. +**Priority:** Medium-high. Ergonomic configuration management is a +core value proposition. + +### Security Model and Updates + +**Current state:** Store paths are content-addressed. FreeBSD sources +can be SHA256-pinned. No signing, no binary cache, no substitute +mechanism, no trust model. + +**Gaps:** +- No cryptographic signing of store paths or closures +- No binary substitute/cache infrastructure +- No trust model for third-party packages +- No security advisory integration +- No automatic update mechanism +- No verified boot chain + +**Blocking?** Not for development use. Blocking for any multi-user +or production deployment. +**Upstream reuse?** Guix's substitute mechanism and signing +infrastructure are well-designed and could be adapted. FreeBSD's +`pkg` signing could inform the trust model. +**Priority:** Medium for now, becomes critical before any production +use. + +### Jail and Virtualisation Integration + +**Current state:** Jails are mentioned in the bootstrap reports +(jail-based build isolation was explored) but no jail management is +integrated into the system model or Shepherd services. bhyve is +blocked by nested VT-x under Xen. + +**Gaps:** +- No `jail` record type in the operating-system model +- No Shepherd service for jail lifecycle management +- No jail-specific store sharing (read-only nullfs mounts of + `/frx/store` into jails) +- No jail networking integration (vnet, epair) +- No `fruix jail` subcommands +- No bhyve integration + +**Blocking?** Not for basic use, but jails are a flagship FreeBSD +feature and a major reason to choose FreeBSD. Users will expect +them. +**Upstream reuse?** FreeBSD's `jail(8)` and `jail.conf(5)` are the +foundation. Guix has no jail equivalent. This must be designed from +scratch, but the operating-system model provides a natural extension +point. +**Priority:** Medium-high. Jail integration is part of the value +proposition. A basic `jail` record type with Shepherd lifecycle +services should be an early post-MVP feature. + +### Networking Configuration + +**Current state:** `rc-conf-entries` in the operating-system model +handle network interface configuration via the traditional rc.conf +mechanism. No declarative networking model exists. + +**Gaps:** +- No first-class network interface declarations in the system model +- No declarative firewall (pf/ipfw) configuration +- No VLAN, bridge, or lagg configuration +- No wireless configuration +- No DNS resolver configuration beyond rc.conf +- Network configuration split between rc.conf entries and potential + Shepherd services + +**Blocking?** Partially. Basic DHCP works via rc.conf. Static +configuration works via rc.conf entries. But the lack of a +declarative network model means networking is the least +"Guix-like" part of the system. +**Upstream reuse?** Guix's networking services (for Linux) are a +structural model. FreeBSD's rc.conf networking is the current +backend and works. The gap is the declarative layer on top. +**Priority:** Medium. Networking works via rc.conf bridge. A +declarative model is important for the vision but not blocking. + +### User-Facing Tooling + +**Current state:** The `fruix` CLI exists with subcommands for +system building, deployment, source management, and native builds. +Node-local commands exist for build, reconfigure, switch, rollback, +and status. No `guix pull` equivalent. + +**Gaps:** +- No channel/repository update mechanism (`guix pull` equivalent) +- No user-level package management (`guix install` equivalent) +- No user profiles or per-user environments +- No `fruix search` for finding packages +- No garbage collection command (`fruix gc`) +- No generation listing or management UI +- No system diff/dry-run tooling + +**Blocking?** Partially. The system can be managed but the +experience is manual and requires understanding internals. +**Upstream reuse?** Guix's CLI is the direct model for all of +these. The commands exist in Guix; they need FreeBSD-specific +implementations. +**Priority:** High for `gc` and generation management. Medium for +user profiles. Channel updates become critical when there is a +package ecosystem to update. + +### Release Engineering / Reproducible Build Infrastructure + +**Current state:** The system can produce disk images and ISOs. +No CI, no automated testing, no release process, no reproducibility +verification. + +**Gaps:** +- No CI/CD pipeline +- No automated build-and-boot testing +- No reproducibility verification (build twice, compare) +- No release versioning scheme +- No changelog generation +- No upgrade path between releases + +**Blocking?** Not for development. Blocking for any claim of +"reproducibility" — without verification, it is an aspiration, not +a property. +**Upstream reuse?** Guix's build farm (Cuirass) is the model but +is Linux-specific. FreeBSD's release engineering toolchain could +inform the process. +**Priority:** Medium. Should be established before the project +seeks external users. + +### Testing and CI + +**Current state:** A collection of shell-script tests in +`fruix-bootstrap/tests/` covering build system phases, native +builds, and system materialisation. Validation is done manually on +XCP-ng VMs and locally via QEMU. No automated CI. + +**Gaps:** +- No automated test suite that runs on commit +- No boot-level testing (VM boots, services come up, assertions pass) +- No unit tests for Scheme modules +- No integration tests for the CLI +- No regression tests for store-path identity stability + +**Blocking?** Not for a solo developer. Blocking for accepting +contributions — without CI, contributions cannot be validated. +**Upstream reuse?** Guix's test infrastructure is extensive but +Linux-specific. FreeBSD's CI (Phabricator/GitHub Actions) could +host the pipeline. +**Priority:** High. Even a minimal "build an image and boot it in +QEMU" CI pipeline would significantly improve development velocity +and confidence. + +--- + +## Overall Verdict + +**This project is worth pursuing.** The philosophical fit between +FreeBSD and Guix is stronger than it appears on the surface, and +the implementation demonstrates that the core ideas translate +cleanly. The codebase shows thoughtful engineering: the store-path +design, the three-layer profile model, the executor abstraction, +the source-provenance tracking, and the safety boundaries around +storage operations are all evidence of considered design rather than +expedient hacking. + +The project has crossed the hardest threshold: proving that the +hybrid *can work*. Shepherd runs as PID 1 on FreeBSD. The store +semantics function. Systems can be declared, built, deployed, and +rolled back. Installer media can be produced. The remaining work is +substantial but straightforward — it is engineering, not research. + +### The Single Most Important Next Step + +**Write the conceptual guide.** The project's biggest risk right now +is not technical — it is legibility. A capable developer encountering +this project today would face 93 markdown files, 44 Scheme modules, +and no clear entry point explaining what the system is, how it works, +or how to use it. The phase reports are a development journal, not +documentation. + +A 10-15 page document explaining the architecture, the mental model, +and the basic workflows would: +1. Enable contributors to onboard without archaeology +2. Force design decisions to be stated explicitly (which sometimes + reveals inconsistencies worth fixing) +3. Provide a foundation for all other documentation +4. Make the project presentable to the FreeBSD and Guix communities + +The second priority is the bootstrap experience. A contributor should +be able to go from "I have a FreeBSD machine" to "I have built and +booted a Fruix image" in under an hour, with clear instructions at +every step. The current bootstrap pipeline works but is not documented +as a user-facing workflow. + +Everything else — more packages, more services, jails, ZFS, CI — is +important but secondary. Without documentation and an accessible +onboarding path, the project remains a brilliant proof of concept that +only its author can operate.