# 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