docs: Opus review

This commit is contained in:
2026-04-08 10:52:02 +02:00
parent 896d7eb598
commit c2faa68ad4
+599
View File
@@ -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.