Document self-hosted development workflow

This commit is contained in:
2026-04-16 14:32:29 +02:00
parent f43d1d1d0d
commit fd5e2285bf
2 changed files with 409 additions and 0 deletions
+4
View File
@@ -39,6 +39,10 @@ The direct checkout entrypoint now also prefers a prepared builder root at:
when `fruix-bootstrap` has already prepared one.
## Guides
- `docs/self-hosted-dev.md` — build the self-hosted development installer ISO and prepare an installed Fruix node for development work
## Intended lifecycle
```text
+405
View File
@@ -0,0 +1,405 @@
# Self-hosted Fruix development
This guide describes the current human-facing path for:
1. building a Fruix installer ISO for the self-hosted development system
2. installing that system onto a VM or machine
3. turning the installed node into a practical Fruix development host
This path is now validated most strongly in VM workflows, especially the
current XCP-ng loop, but the same basic process applies to any machine that can
boot the generated installer ISO.
## What this system is for
`examples/system/self-hosted-dev.scm` defines a Fruix node intended to:
- run Fruix locally
- expose SSH for operator access
- provide a development profile with the current editor/runtime tooling
- provide a sanitized build profile for native base/package work
- serve as a host for the pi-agent-style development workflow
Today that development profile includes the currently recovered developer tools,
notably:
- Clang toolchain
- GNU make + FreeBSD make files
- Autotools
- OpenSSL + zlib
- sh + bash
- Node.js + npm
- ripgrep
- tmux
- neovim
## Before you build
### 1. Prepare a Fruix builder host
The commands below assume:
- you are on a FreeBSD host
- `fruix-bootstrap` has already prepared a builder root
- this repo is checked out locally
The checkout entrypoint prefers a prepared builder root at:
- `~/.local/opt/fruix-builder`
So if that builder exists, you can usually invoke Fruix from the checkout with:
```sh
./bin/fruix ...
```
### 2. Copy the example declaration and customize it
Start from the example:
```sh
cp examples/system/self-hosted-dev.scm my-self-hosted-dev.scm
```
At minimum, edit:
- `#:host-name`
- `#:root-authorized-keys` for remote SSH access
- any user/account details you want to change
A minimal customization usually looks like:
```scheme
(use-modules (fruix system freebsd)
(fruix packages freebsd))
(define self-hosted-development-operating-system
(operating-system
#:host-name "fruix-dev-1"
#:rc-conf-entries '(("clear_tmp_enable" . "YES")
("sendmail_enable" . "NONE")
("sshd_enable" . "YES"))
#:root-authorized-keys
'("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... you@example")
#:development-packages %freebsd-development-profile-packages
#:build-packages %freebsd-development-profile-packages))
```
If you skip `#:root-authorized-keys`, the installed node may still be usable
from the console, but it will not be ready for the normal remote development
loop.
### 3. Pick a store directory with real space
Image and installer builds can be large. Prefer a store directory with plenty of
space, for example:
```sh
mkdir -p /var/tmp/fruix-selfhosted-store
```
## Build the installer ISO
Run:
```sh
./bin/fruix system installer-iso ./my-self-hosted-dev.scm \
--system self-hosted-development-operating-system \
--store /var/tmp/fruix-selfhosted-store \
> /tmp/self-hosted-installer-iso.out
```
The command prints metadata rather than just one path. Recover the ISO path
from the output like this:
```sh
iso=$(sed -n 's/^iso_image=//p' /tmp/self-hosted-installer-iso.out | sed -n '$p')
printf 'ISO: %s\n' "$iso"
```
Other useful fields in that output are:
- `iso_store_path=`
- `installer_closure_path=`
- `target_closure_path=`
- `installer_state_path=`
If you want to inspect the whole result later, keep the metadata file:
```sh
less /tmp/self-hosted-installer-iso.out
```
## Boot and install
### 1. Boot the ISO
Use the generated `installer.iso` in whichever way is convenient for your
hardware or VM platform:
- attach it as virtual media in a VM
- boot it as a CD/DVD image
- or write it to removable media using your usual FreeBSD workflow
For development use, give the target disk enough headroom. In practice,
something in the **20-40 GiB** range is a much better starting point than a
minimal test disk.
### 2. Use the installer
The current intended human path from the booted installer is:
```sh
/usr/local/bin/fruix system installer-tui
```
That starts the current experimental Newt-based installer flow.
Notes:
- this is still marked experimental
- the ISO was built from your declaration, so this is the path to try first
- if you are installing in a VM, use the VM console for the TUI
### 3. If you need the direct CLI install path
If you want an explicit non-interactive install from the booted installer
environment, use `fruix system install` and pass the declaration file
explicitly.
Direct installs to `/dev/...` targets are intentionally gated. In the installer
shell, when you are already root, the simplest current pattern is:
```sh
export FRUIX_ASSEMBLY_PRIVILEGED_COMMAND=env
export FRUIX_ASSEMBLY_ALLOW_BLOCK_DEVICE_TARGETS=1
/usr/local/bin/fruix system install /run/current-system/metadata/system-declaration.scm \
--system self-hosted-development-operating-system \
--target /dev/vtbd0
```
Adjust `/dev/vtbd0` to the real target device.
If you use this path, remember:
- it is destructive for the selected target
- block-device installs are opt-in on purpose
- the TUI path is the friendlier default for humans
## First boot after installation
Once the installed system boots, log in on the console or over SSH and do a
quick sanity check:
```sh
/usr/local/bin/fruix system status
```
You should see metadata describing the current generation, closure, and default
declaration paths.
If you added root SSH keys in the declaration, verify remote access from your
operator machine.
## Development environment on the installed node
The installed self-hosted system already includes two important helper scripts.
### 1. General development shell
For editor/runtime/package-development work:
```sh
eval "$(/usr/local/bin/fruix-development-environment)"
```
That exports a development-oriented environment pointing at:
- `/run/current-system/development-profile`
- compiler/toolchain paths
- Node/npm paths
- runtime library paths
- man pages and other tool metadata
After enabling it, useful smoke checks are:
```sh
cc --version
node --version
npm --version
rg --version
tmux -V
nvim --version | sed -n '1p'
```
### 2. Sanitized build shell
For native FreeBSD base rebuild work and stricter build testing:
```sh
eval "$(/usr/local/bin/fruix-build-environment)"
```
That switches to the build-profile-oriented environment and drops the normal
development-shell overrides that would interfere with more controlled builds.
Useful quick checks:
```sh
echo "$FRUIX_BUILD_PROFILE"
make -V .CURDIR >/dev/null
```
### 3. Optional native base rebuild helper
The self-hosted system also installs:
- `/usr/local/bin/fruix-self-hosted-native-build`
and exposes it through:
```sh
/usr/local/bin/fruix system build-base
```
That is the current node-local helper for rebuilding FreeBSD world/kernel
artifacts from the staged source tree. It is useful, but it is a heavier,
longer-running workflow than ordinary declaration or package iteration.
## Working on Fruix itself from the installed node
There are two different Fruix entrypoints on an installed self-hosted node, and
it helps to use the right one.
### 1. The installed node CLI
```sh
/usr/local/bin/fruix
```
Use this for node-local lifecycle operations such as:
- `fruix system status`
- `fruix system reconfigure`
- `fruix system rollback`
- `fruix system switch`
This CLI is bundled into the installed system closure and is the right tool for
managing the node you are currently running.
### 2. The checkout CLI
If you are editing Fruix source code in a working tree, use the checkout
entrypoint from that tree:
```sh
/path/to/fruix/bin/fruix
```
That path evaluates the Fruix source in your checkout and is what you want when
changing:
- Fruix modules
- package definitions
- system render logic
- installer logic
- build/materialization code
### 3. Getting a checkout onto the node
The current self-hosted development profile does **not** yet provide a packaged
`git`, so the easiest current options are:
- copy an existing checkout onto the node with `scp`/`rsync`
- mount or share a dataset that contains the checkout
- or add your own temporary host-side transfer step
### 4. Preparing the checkout environment
For checkout-based work, prepare the node the same way as any other Fruix build
host:
- create or copy in a Fruix checkout
- prepare `~/.local/opt/fruix-builder` using `fruix-bootstrap`
- run the checkout via `./bin/fruix`
The checkout entrypoint already prefers `~/.local/opt/fruix-builder` when it is
present.
## A practical day-to-day loop
### Declaration-only changes
If you are only changing the system declaration used by the running node:
```sh
/usr/local/bin/fruix system reconfigure /path/to/my-self-hosted-dev.scm \
--system self-hosted-development-operating-system
```
That builds a new closure using the node's bundled Fruix payload and switches
node metadata to the new generation.
After a successful reconfigure, plan on a reboot.
If you need to undo it:
```sh
/usr/local/bin/fruix system rollback
```
### Fruix source-tree changes
If you changed Fruix code in a checkout, build with that checkout:
```sh
/path/to/fruix/bin/fruix system build /path/to/my-self-hosted-dev.scm \
--system self-hosted-development-operating-system \
--store /var/tmp/fruix-dev-store \
> /tmp/fruix-build.out
```
Recover the closure path:
```sh
closure=$(sed -n 's/^closure_path=//p' /tmp/fruix-build.out | sed -n '$p')
printf 'closure: %s\n' "$closure"
```
Then switch the running node to that closure:
```sh
/usr/local/bin/fruix system switch "$closure"
```
Again, plan on a reboot after switching.
## Recommended post-install checklist
For a node that should serve as a real self-hosted Fruix development box, the
useful immediate checklist is:
- [ ] confirm SSH access works with your real operator key
- [ ] run `/usr/local/bin/fruix system status`
- [ ] enable the development environment and check `cc`, `node`, `npm`, `tmux`, and `nvim`
- [ ] copy a Fruix checkout onto the machine
- [ ] prepare `~/.local/opt/fruix-builder` on the node
- [ ] verify the checkout entrypoint works: `./bin/fruix --help`
- [ ] keep a scratch store directory for checkout builds, e.g. `/var/tmp/fruix-dev-store`
- [ ] do one full `reconfigure` and one `rollback` before treating the node as a daily driver
## Current limitations and expectations
This workflow is useful now, but it is still an actively developing path.
Important current expectations:
- the self-hosted path is real and usable, but not yet the final polished Fruix
product workflow
- the installed-node lifecycle is farther along than the broader deploy/upgrade
story
- the development profile is practical, but still intentionally small and
transitional
- some rough edges remain; treat the current system as a serious development
environment, not yet a finished distribution experience