# Poudriere + bhyve Build Server Plan — v2.0.0 > Build colibri/clawdie as proper FreeBSD packages instead of raw `cargo build` > binaries copied into the ISO. Plus bhyve virtualization for build test VMs. > Target: the **`mother-build`** host (FreeBSD VPS) serving **`pkg.clawdie.si`** — > the package half of the trusted supply chain (layered-soul `HIVE-ONBOARDING.md §10`). **Date:** 4 Jun 2026 (v1.0.0) · retargeted 20 Jun 2026 (v2.0.0) **Target server:** `mother-build` — a FreeBSD VPS with a ZFS pool (OVHcloud candidate; see layered-soul `HOST-MATRIX.md`). The original ML350p Gen8 target is **retired** (boot-looping); host provisioning is now provider-specific, not iLO/IPMI. Hardware- specific sizing below (32 GB split, Xeon VT-x) is kept only as **reference numbers** from the ML350p — scale them to the chosen `mother-build` plan. **Scripts:** the reproducible Phase 2–3 steps are implemented in [`../scripts/poudriere/`](../scripts/poudriere/) — `poudriere-setup.sh`, `poudriere-build.sh`, `clawdie-repo.conf.in`. This doc is the rationale, host- provisioning context, and the manual reference those scripts encode. --- ## Why Current build flow: ``` colibri host (dev machine) cargo build --release → stage-colibri-iso.sh copies binaries to ISO → no version tracking, no pkg metadata, manual ``` Proposed flow: ``` mother-build server (Poudriere) → FreeBSD port: sysutils/colibri → poudriere builds .pkg files in clean jail → signed pkg repo hosted at pkg.clawdie.si → ISO build does: pkg install colibri ``` Benefits: - Versioned packages with dependencies - Clean-room builds (no host contamination) - `pkg upgrade colibri` on deployed machines - No Rust toolchain needed on the ISO build host - Proper `pkg info colibri` metadata ## Memory split (reference sizing — ML350p 32 GB) These numbers are the original ML350p reference; on a smaller cloud `mother-build` plan, poudriere alone needs little (drop `USE_TMPFS` if RAM is tight) and the bhyve guests are optional. 32GB total — 16GB host, 16GB bhyve: ``` Host (16GB): 6GB — ZFS ARC (primary cache) 4GB — Poudriere tmpfs (builds in RAM) 6GB — headroom (ARC bursts, nginx, clawdie, SSH) bhyve (16GB): 4GB — FreeBSD build jail (Poudriere test guest) 4GB — Linux test guest (cross-compile validation) 4GB — FreeBSD ISO test VM (boot clawdie-iso after build) 4GB — spare / future CI runner ``` CPU: Xeon E5-2400 v1/v2 — all SKUs have VT-x + EPT + VT-d. bhyve uses hardware-accelerated virt (no emulation penalty). With 8-10 cores, 2 vCPUs per guest is comfortable. ## Architecture ``` ┌──────────────────────────────────────────────────┐ │ mother-build host (FreeBSD 15) │ │ │ │ ZFS pool: zroot │ │ zroot/ROOT/default ← base system │ │ zroot/poudriere ← build jails │ │ zroot/poudriere/data ← packages │ │ zroot/poudriere/ports ← ports tree │ │ zroot/bhyve ← VM disk images │ │ zroot/bhyve/iso-test ← ISO boot test VM │ │ │ │ Services: │ │ poudriere (bulk builder) │ │ bhyve (VM host: FreeBSD + Linux + ISO test) │ │ nginx (pkg repo) │ │ clawdie agent (self-hosting) │ │ tailscale (mesh) │ └──────────────────────────────────────────────────┘ ``` ## Phase 1 - server provision (provider-specific) ### 1.1 Get a booted FreeBSD host Host provisioning is **provider-specific** and out of scope for the scripts. On OVHcloud, install FreeBSD via the rescue system or a FreeBSD template/image; the old ML350p iLO/IPMI virtual-media path is retired. The poudriere scripts assume you already have a **booted FreeBSD host with a ZFS pool** (default `zroot`). ### 1.2 Disk survey + ZFS ```sh camcontrol devlist geom disk list # Single disk or mirror zpool create -o ashift=12 zroot /dev/ada0 zfs create -o mountpoint=/ zroot/ROOT/default zfs create zroot/poudriere zfs create -o mountpoint=/usr/local/poudriere zroot/poudriere/root ``` ### 1.3 Install base system ```sh # Install FreeBSD 15 base + kernel to zroot # Set up /boot, /etc, and the future deployed-system service hook # Configure network (DHCP, tailscale) ``` ## Phase 2 - Poudriere setup > **Scripted (DONE).** `poudriere-setup.sh` performs 2.1–2.3 idempotently > (install, `poudriere.conf` incl. `PKG_REPO_SIGNING_KEY`, signing-key generation, > jail, ports tree) after validating root / version / pool. `poudriere-build.sh` > performs 2.5 (bulk + automatic repo signing). The steps below are the reference > they encode; only 2.4 (creating the `sysutils/colibri` port) is still manual. ### 2.1 Install Poudriere ```sh pkg install poudriere ``` ### 2.2 Configure ```sh # /usr/local/etc/poudriere.conf ZPOOL=zroot ZROOTFS=/poudriere FREEBSD_HOST=download.freebsd.org RESOLV_CONF=/etc/resolv.conf BASEFS=/usr/local/poudriere USE_TMPFS=yes # build in RAM (we have 32GB) MAX_MEMORY=4 # limit per-jail RAM MAX_FILES=2048 DISTFILES_CACHE=/usr/local/poudriere/distfiles PARALLEL_JOBS=8 ``` ### 2.3 Create jail + ports tree ```sh # poudriere-setup.sh does this with these names by default: poudriere jail -c -j clawdie-amd64 -v 15.0-RELEASE -a amd64 poudriere ports -c -p clawdie -m git -B main ``` ### 2.4 Colibri port — canonical in the colibri repo The `sysutils/colibri` port is **owned by the colibri repo**, kept with the code it builds at `packaging/freebsd/port/sysutils/colibri/`: ``` sysutils/colibri/ ├── Makefile USES=cargo; ships 6 binaries + rc.d services ├── pkg-descr ├── pkg-plist └── files/ rc.d templates (colibri_daemon.in, colibri_bridge.in) ``` Copy that directory into the poudriere ports tree before building. **This repo keeps no duplicate** — `build.sh`'s release gate fails if `ports/sysutils/colibri/` reappears here. Key facts: - `LICENSE= MIT` (per `colibri/Cargo.toml`; same as layered-soul). - `USES= cargo`; source from the Forgejo archive (tagged `v${DISTVERSION}`). - Ships 6 binaries: `clawdie`, `colibri`, `colibri-daemon`, `colibri-mcp`, `colibri-test-agent`, `colibri-tui` — plus the `colibri_daemon` / `colibri_bridge` rc.d services. - `CARGO_CRATES` is committed and kept in sync with `Cargo.lock` by `check-cargo-crates.sh` (colibri CI). `distinfo` is generated on the build host with `make makesum`. ### 2.5 Build ```sh cd /usr/local/poudriere/ports/clawdie/sysutils/colibri make makesum # generate distinfo make cargo-crates > Makefile.crates # generate crate list # then build via the wrapper (validates + signs): poudriere-build.sh --jail clawdie-amd64 --ports clawdie sysutils/colibri ``` ## Phase 3 - pkg repository ### 3.1 Nginx pkg repo ```sh pkg install nginx # /usr/local/etc/nginx/nginx.conf: # server { listen 443 ssl; server_name pkg.clawdie.si; # root /usr/local/poudriere/data/packages/clawdie-amd64-clawdie; autoindex on; } # TLS via acme.sh (osa/mother-build already carry nginx + acme). service nginx enable service nginx start ``` ### 3.2 Client config On ISO builds and deployed jails, generate from [`../scripts/poudriere/clawdie-repo.conf.in`](../scripts/poudriere/clawdie-repo.conf.in) and ship the **public** key from Phase 2. The repo is **signed**, so clients verify: ```sh sed "s#__PKG_URL__#https://pkg.clawdie.si/#; s#__PUBKEY_PATH__#/usr/share/keys/pkg/clawdie.pub#" \ clawdie-repo.conf.in > /usr/local/etc/pkg/repos/clawdie.conf install -m 0444 /usr/local/etc/ssl/clawdie-pkg.pub /usr/share/keys/pkg/clawdie.pub ``` For first-party-only (paid) tenants, lower the stock FreeBSD repo priority so resolution prefers signed Clawdie packages. ## Phase 4 - ISO integration Instead of `stage-colibri-iso.sh` copying raw binaries: ```sh # build.sh - install colibri packages during ISO build pkg -r ${MOUNT_POINT} install colibri ``` This gives us: - `colibri` - `colibri-daemon` - `colibri-test-agent` - `colibri-mcp` - `colibri-tui` (optional in current raw-binary staging, desired for operator USB) All with proper pkg metadata, upgradeable, with dependencies tracked. ## Phase 5 — self-hosted clawdie Once the deployed-system service implementation lands and the server builds itself: ```sh # The mother-build host runs its own deployed-system clawdie service service clawdie enable service clawdie start # clawdie monitors build jails, runs periodic rebuilds # clawdie skills include "rebuild-colibri" and "poudriere-status" ``` The current live USB does not stage `service clawdie`; it runs `colibri_daemon` directly. ## Phase 6 — bhyve test VMs > **Largely DONE (on the ISO-build host, not yet on `mother-build`).** The > ISO-boot test VM exists and is wired as a **build gate**: > [`../scripts/bhyve-test.sh`](../scripts/bhyve-test.sh) boots the built image in > bhyve with `com1,stdio`, and [`../scripts/run-bhyve-test.sh`](../scripts/run-bhyve-test.sh) > scans the serial console for required boot markers / forbidden failures (panics, > restart loops, the colibri permission-denied regression) and exits non-zero on a > critical miss. [`../scripts/bhyve-pf-allow.sh`](../scripts/bhyve-pf-allow.sh) opens > the guest network. What remains: replicate this on `mother-build` and add the > Linux cross-compile / poudriere-validation guests (6.4) if wanted. ### 6.1 Enable bhyve kernel module ```sh kldload vmm sysrc kld_list+="vmm" ``` ### 6.2 Install bhyve packages ```sh pkg install bhyve-firmware edk2-bhyve vm-bhyve ``` ### 6.3 Create ZFS datasets for VMs ```sh zfs create zroot/bhyve zfs create zroot/bhyve/iso-test zfs create zroot/bhyve/linux-test zfs create zroot/bhyve/freebsd-test ``` ### 6.4 Test VMs **FreeBSD ISO test VM** (boots clawdie-iso after each build): ```sh vm create -t freebsd iso-test vm install iso-test clawdie-iso.iso vm start iso-test # → verifies ISO boots, colibri starts, clawdie health passes ``` **Linux cross-compile test VM** (validates non-FreeBSD targets): ```sh vm create -t linux linux-test # → test colibri builds on Linux target ``` **FreeBSD Poudriere test jail VM** (full pkg build validation): ```sh vm create -t freebsd freebsd-test # → clone poudriere setup, run bulk build as validation ``` ### 6.5 Packages for ISO Add to `pkg-list-disk-install-extras.txt` (installed on deployed server, not live USB): ``` bhyve-firmware edk2-bhyve vm-bhyve ``` ## Timeline | Step | Effort | Status / depends on | | -------------------------------------- | -------------- | --------------------------------------------------- | | 1. Provision `mother-build` host | ~1h | PENDING — OVH FreeBSD VPS + ZFS pool (cost-gated) | | 2. Poudriere setup | ~30m | **SCRIPTED** — `poudriere-setup.sh`; needs the host | | 3. colibri port creation | ~1h | PENDING — write `sysutils/colibri` (manual, §2.4) | | 4. First pkg build | ~30m (compile) | **SCRIPTED** — `poudriere-build.sh`; needs the port | | 5. pkg repo + nginx (`pkg.clawdie.si`) | ~15m | PENDING — nginx + acme + DNS | | 6. ISO integration | ~15m | PENDING — `pkg install` in `build.sh` (Phase 4) | | 7. bhyve ISO-boot test gate | done | **DONE** — `bhyve-test.sh` + `run-bhyve-test.sh` | **Critical path:** provision `mother-build` → write the colibri port → run the two scripts → stand up nginx/DNS. The host purchase is gated on the HOST-MATRIX cost rows. ## Notes - Rust target `x86_64-unknown-freebsd` stays — that's the compiler triple. The package name is `colibri` (or `clawdie-colibri`), no "unknown" in package branding. - Poudriere builds each package in a clean jail — no host Rust toolchain pollution. The jail installs `lang/rust` from ports automatically. - `USE_TMPFS=yes` keeps builds in RAM (fast, no SSD wear) when the host has the memory; drop it on a small cloud plan. - Tailscale mesh means the operator USB can install packages from this server even after deploying to a different machine. - bhyve needs a host with VT-x + EPT (the ML350p's Xeon E5-2400 had it; confirm the chosen OVH plan exposes nested virt before relying on Phase 6 there).