From 2dac2d108d75988628ebd236b0a4e069ffded776 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sat, 20 Jun 2026 13:19:59 +0200 Subject: [PATCH 1/4] feat(poudriere): lean scripts for first-party pkg.clawdie.si build server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns the manual Phase 2–3 runbook (docs/POUDRIERE-BUILD-SERVER.md) into repeatable, idempotent steps for the mother-build host — the package half of the trusted supply chain (layered-soul HIVE-ONBOARDING §10). - poudriere-setup.sh: verify-then-act setup. Validates root, FreeBSD version format, pkg/openssl, and that the ZFS pool exists BEFORE acting; then installs poudriere, generates the repo signing key (0400), writes poudriere.conf (only if absent), and creates the build jail + ports tree. Re-running skips anything already present. - poudriere-build.sh: validates jail, ports tree, and each origin (category/name + Makefile present) before `poudriere bulk`; repo is signed automatically via PKG_REPO_SIGNING_KEY. - clawdie-repo.conf.in: client repo template (signature_type pubkey) + the first-party-only priority note. - README.md: the three-step flow and conventions. Style matches live/operator-session/hw-report: POSIX sh, set -u, fixed PATH, strict arg parsing, minimal checks (only what is acted upon). Host provisioning (ZFS/base/network) stays in the runbook — these assume a FreeBSD host with a pool. sh -n clean. Co-Authored-By: Claude Opus 4.8 --- scripts/poudriere/README.md | 58 +++++++++++ scripts/poudriere/clawdie-repo.conf.in | 18 ++++ scripts/poudriere/poudriere-build.sh | 72 +++++++++++++ scripts/poudriere/poudriere-setup.sh | 139 +++++++++++++++++++++++++ 4 files changed, 287 insertions(+) create mode 100644 scripts/poudriere/README.md create mode 100644 scripts/poudriere/clawdie-repo.conf.in create mode 100755 scripts/poudriere/poudriere-build.sh create mode 100755 scripts/poudriere/poudriere-setup.sh diff --git a/scripts/poudriere/README.md b/scripts/poudriere/README.md new file mode 100644 index 00000000..843e1864 --- /dev/null +++ b/scripts/poudriere/README.md @@ -0,0 +1,58 @@ +# First-party package build server (`pkg.clawdie.si`) + +Lean scripts that turn the manual Phase 2–3 runbook in +[`../../docs/POUDRIERE-BUILD-SERVER.md`](../../docs/POUDRIERE-BUILD-SERVER.md) into +repeatable, idempotent steps. They run on the **`mother-build`** host (FreeBSD) and +produce the **first-party, signed** package repo that paid tenants pull from instead +of public mirrors — the package half of the trusted supply chain +([layered-soul `HIVE-ONBOARDING.md §10`](https://code.smilepowered.org/clawdie/layered-soul)). + +These scripts deliberately do not provision the host (ZFS pool, base system, +networking) — that is hardware-specific and stays in the runbook. They assume a +FreeBSD host with a ZFS pool already present. + +## Flow + +``` +poudriere-setup.sh → poudriere-build.sh → serve + client config +(config, key, jail, (build ports into the (nginx over the repo dir; + ports tree) signed repo) clawdie-repo.conf.in) +``` + +1. **Set up** (idempotent; re-run anytime): + + ```sh + mdo -u root ./poudriere-setup.sh --zpool zroot + ``` + + Generates `/usr/local/etc/ssl/clawdie-pkg.{key,pub}`, writes + `/usr/local/etc/poudriere.conf` (only if absent), and creates the build jail and + ports tree. The **public** key is what clients trust. + +2. **Build** (signs the repo automatically via `PKG_REPO_SIGNING_KEY`): + + ```sh + mdo -u root ./poudriere-build.sh --jail clawdie-amd64 --ports clawdie sysutils/colibri + ``` + + The `sysutils/colibri` port itself (Makefile/distinfo/pkg-plist) is created in the + ports tree per the runbook's Phase 2.4. + +3. **Serve + clients.** Point nginx at + `/usr/local/poudriere/data/packages/clawdie-amd64-clawdie` for `https://pkg.clawdie.si/` + (osa/mother-build already carry `nginx` + `acme`). Generate each client's repo config + from the template and ship the public key: + + ```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 + ``` + +## Conventions + +- POSIX `sh`, `set -u`, fixed `PATH`, `usage()` + strict arg parsing, **verify-then-act** + (inputs and environment are validated before anything is created). Matches + `live/operator-session/hw-report`. +- Idempotent: existing config, key, jail, and ports tree are left untouched. +- Defaults are overridable by flag or `POUDRIERE_*` env var. diff --git a/scripts/poudriere/clawdie-repo.conf.in b/scripts/poudriere/clawdie-repo.conf.in new file mode 100644 index 00000000..c60baec1 --- /dev/null +++ b/scripts/poudriere/clawdie-repo.conf.in @@ -0,0 +1,18 @@ +# First-party Clawdie package repo. Install on clients (ISO build root and +# deployed jails) as /usr/local/etc/pkg/repos/clawdie.conf, after substituting +# __PKG_URL__ and shipping the public key to __PUBKEY_PATH__. +# +# sed "s#__PKG_URL__#https://pkg.clawdie.si/#" clawdie-repo.conf.in > clawdie.conf +# install -m 0444 clawdie-pkg.pub __PUBKEY_PATH__ # from poudriere-setup.sh +# +# For first-party-only (paid) tenants, also set the stock FreeBSD repo to a +# lower priority (or disabled) so resolution prefers signed Clawdie packages. + +clawdie: { + url: "__PKG_URL__", + mirror_type: "none", + signature_type: "pubkey", + pubkey: "__PUBKEY_PATH__", + priority: 100, + enabled: yes +} diff --git a/scripts/poudriere/poudriere-build.sh b/scripts/poudriere/poudriere-build.sh new file mode 100755 index 00000000..05ef2b48 --- /dev/null +++ b/scripts/poudriere/poudriere-build.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# Build one or more ports into the first-party repo with poudriere, then refresh +# repo metadata (signed via PKG_REPO_SIGNING_KEY in poudriere.conf). Validates +# the jail, ports tree, and each origin before building. Run as root. + +set -u +PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +export PATH + +BASEFS="${POUDRIERE_BASEFS:-/usr/local/poudriere}" +JAIL="${POUDRIERE_JAIL:-clawdie-amd64}" +PTREE="${POUDRIERE_PTREE:-clawdie}" + +usage() { + cat <&2 + exit 1 +} + +have() { + command -v "$1" >/dev/null 2>&1 +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --jail) JAIL="${2:?--jail needs a value}"; shift 2 ;; + --ports) PTREE="${2:?--ports needs a value}"; shift 2 ;; + -h|--help) usage; exit 0 ;; + --) shift; break ;; + -*) echo "ERROR: unknown option: $1" >&2; usage >&2; exit 2 ;; + *) break ;; + esac +done + +[ "$#" -ge 1 ] || { echo "ERROR: need at least one ORIGIN" >&2; usage >&2; exit 2; } + +# --- verify, then act ------------------------------------------------------- + +[ "$(id -u)" -eq 0 ] || die "must run as root." +have poudriere || die "poudriere not found; run poudriere-setup.sh first." +poudriere jail -i -j "${JAIL}" >/dev/null 2>&1 || die "jail '${JAIL}' not found; run poudriere-setup.sh." +poudriere ports -l | awk 'NR>1 {print $1}' | grep -Fxq "${PTREE}" || die "ports tree '${PTREE}' not found." + +_ports_root="${BASEFS}/ports/${PTREE}" +for _origin in "$@"; do + case "${_origin}" in + */*) ;; + *) die "origin '${_origin}' must be category/name (e.g. sysutils/colibri)." ;; + esac + [ -f "${_ports_root}/${_origin}/Makefile" ] || \ + die "port '${_origin}' not found under ${_ports_root}." +done + +poudriere bulk -j "${JAIL}" -p "${PTREE}" "$@" || die "bulk build failed." + +echo "" +echo "Build complete. Signed repo at:" +echo " ${BASEFS}/data/packages/${JAIL}-${PTREE}" +echo "Serve that directory over nginx at pkg.clawdie.si (see README.md)." diff --git a/scripts/poudriere/poudriere-setup.sh b/scripts/poudriere/poudriere-setup.sh new file mode 100755 index 00000000..51618669 --- /dev/null +++ b/scripts/poudriere/poudriere-setup.sh @@ -0,0 +1,139 @@ +#!/bin/sh +# Set up a first-party FreeBSD package build server (poudriere) for +# pkg.clawdie.si. Idempotent: safe to re-run. Validates the environment and +# inputs first, then acts. Run on the mother-build host as root. +# +# After this: build with poudriere-build.sh, serve the repo over nginx, and +# hand clients clawdie-repo.conf.in. See README.md. + +set -u +PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +export PATH + +ZPOOL="${POUDRIERE_ZPOOL:-zroot}" +ZROOTFS="${POUDRIERE_ZROOTFS:-/poudriere}" +BASEFS="${POUDRIERE_BASEFS:-/usr/local/poudriere}" +VERSION="${POUDRIERE_VERSION:-15.0-RELEASE}" +ARCH="${POUDRIERE_ARCH:-amd64}" +JAIL="${POUDRIERE_JAIL:-clawdie-${ARCH}}" +PTREE="${POUDRIERE_PTREE:-clawdie}" +PORTS_BRANCH="${POUDRIERE_PORTS_BRANCH:-main}" +KEYDIR="${POUDRIERE_KEYDIR:-/usr/local/etc/ssl}" +KEY="${KEYDIR}/clawdie-pkg.key" +PUB="${KEYDIR}/clawdie-pkg.pub" +CONF="/usr/local/etc/poudriere.conf" + +usage() { + cat <&2 + exit 1 +} + +have() { + command -v "$1" >/dev/null 2>&1 +} + +info() { + printf '==> %s\n' "$*" +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --zpool) ZPOOL="${2:?--zpool needs a value}"; shift 2 ;; + --version) VERSION="${2:?--version needs a value}"; shift 2 ;; + --arch) ARCH="${2:?--arch needs a value}"; shift 2 ;; + --jail) JAIL="${2:?--jail needs a value}"; shift 2 ;; + --ports) PTREE="${2:?--ports needs a value}"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) echo "ERROR: unknown option: $1" >&2; usage >&2; exit 2 ;; + esac +done + +# --- verify, then act ------------------------------------------------------- + +[ "$(id -u)" -eq 0 ] || die "must run as root (poudriere setup creates jails)." + +case "${VERSION}" in + [0-9]*.[0-9]*-RELEASE|[0-9]*.[0-9]*-STABLE|[0-9]*.[0-9]*-CURRENT) ;; + *) die "invalid --version '${VERSION}' (expected e.g. 15.0-RELEASE)." ;; +esac + +have pkg || die "pkg not found; run on a FreeBSD host." +have openssl || die "openssl not found (needed to generate the repo signing key)." +zpool list "${ZPOOL}" >/dev/null 2>&1 || die "ZFS pool '${ZPOOL}' not found; create it or pass --zpool." + +# --- act -------------------------------------------------------------------- + +if ! have poudriere; then + info "installing poudriere" + env ASSUME_ALWAYS_YES=yes pkg install -y poudriere || die "pkg install poudriere failed." +fi + +if [ ! -f "${KEY}" ]; then + info "generating repo signing key: ${KEY}" + mkdir -p "${KEYDIR}" + ( umask 077; openssl genrsa -out "${KEY}" 4096 ) || die "failed to generate signing key." + openssl rsa -in "${KEY}" -pubout -out "${PUB}" || die "failed to derive public key." + chmod 0400 "${KEY}" +fi + +if [ ! -f "${CONF}" ]; then + info "writing ${CONF}" + cat > "${CONF}" </dev/null 2>&1; then + info "jail '${JAIL}' exists; skipping" +else + info "creating jail '${JAIL}' (${VERSION} ${ARCH})" + poudriere jail -c -j "${JAIL}" -v "${VERSION}" -a "${ARCH}" || die "jail create failed." +fi + +if poudriere ports -l | awk 'NR>1 {print $1}' | grep -Fxq "${PTREE}"; then + info "ports tree '${PTREE}' exists; skipping" +else + info "creating ports tree '${PTREE}' (git ${PORTS_BRANCH})" + poudriere ports -c -p "${PTREE}" -m git -B "${PORTS_BRANCH}" || die "ports tree create failed." +fi + +cat < Date: Sat, 20 Jun 2026 13:26:45 +0200 Subject: [PATCH 2/4] docs(poudriere): retarget plan to mother-build + pkg.clawdie.si; mark done work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v1.0.0 → v2.0.0. Fix stale references and cross resolved items from pending: - Target: ML350p Gen8 (retired/boot-looping) → mother-build FreeBSD VPS. - Repo URL: pkg.clawdie.home.arpa → pkg.clawdie.si (signed). Client config now uses signature_type pubkey + ships the public key; points at the clawdie-repo.conf.in template. - Phase 1: drop iLO/IPMI/10.0.0.2 boot path (provider-specific now). - Phase 2/4: marked SCRIPTED — implemented by scripts/poudriere/. - Phase 6 (bhyve): marked DONE — the ISO-boot test gate already exists (scripts/bhyve-test.sh + run-bhyve-test.sh + bhyve-pf-allow.sh). - Jail/tree example names aligned to the scripts (clawdie-amd64 / clawdie). - Timeline rebuilt with real status; ML350p sizing kept only as labeled reference numbers. .home.arpa LIVE-USB hostname refs left intact (not stale). Validation: prettier@3 --check '**/*.md' clean. Co-Authored-By: Claude Opus 4.8 --- docs/POUDRIERE-BUILD-SERVER.md | 121 +++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/docs/POUDRIERE-BUILD-SERVER.md b/docs/POUDRIERE-BUILD-SERVER.md index 62220058..9d9381cc 100644 --- a/docs/POUDRIERE-BUILD-SERVER.md +++ b/docs/POUDRIERE-BUILD-SERVER.md @@ -1,11 +1,21 @@ -# Poudriere + bhyve Build Server Plan — v1.0.0 +# 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: HPE ML350p Gen8, 32GB RAM, Xeon E5-2400. +> 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 -**Target server:** HPE ProLiant ML350p Gen8 (iLO at 10.0.0.2) +**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. --- @@ -23,10 +33,10 @@ colibri host (dev machine) Proposed flow: ``` -ml350p build server (Poudriere) +mother-build server (Poudriere) → FreeBSD port: sysutils/colibri → poudriere builds .pkg files in clean jail - → pkg repo hosted at pkg.clawdie.home.arpa + → signed pkg repo hosted at pkg.clawdie.si → ISO build does: pkg install colibri ``` @@ -38,9 +48,11 @@ Benefits: - No Rust toolchain needed on the ISO build host - Proper `pkg info colibri` metadata -## Memory split +## Memory split (reference sizing — ML350p 32 GB) -32GB total — 16GB host, 16GB bhyve: +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): @@ -63,7 +75,7 @@ With 8-10 cores, 2 vCPUs per guest is comfortable. ``` ┌──────────────────────────────────────────────────┐ -│ HPE ML350p Gen8 (FreeBSD 15) │ +│ mother-build host (FreeBSD 15) │ │ │ │ ZFS pool: zroot │ │ zroot/ROOT/default ← base system │ @@ -82,17 +94,14 @@ With 8-10 cores, 2 vCPUs per guest is comfortable. └──────────────────────────────────────────────────┘ ``` -## Phase 1 - server provision +## Phase 1 - server provision (provider-specific) -### 1.1 Boot FreeBSD from ISO +### 1.1 Get a booted FreeBSD host -```sh -# iLO virtual media mount + boot -ipmitool -H 10.0.0.2 -U Administrator -P power status -ipmitool -H 10.0.0.2 -U Administrator -P chassis bootdev cdrom -# Mount clawdie-iso via iLO virtual media -ipmitool -H 10.0.0.2 -U Administrator -P power reset -``` +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 @@ -117,6 +126,12 @@ zfs create -o mountpoint=/usr/local/poudriere zroot/poudriere/root ## 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 @@ -142,8 +157,9 @@ PARALLEL_JOBS=8 ### 2.3 Create jail + ports tree ```sh -poudriere jail -c -j 150-amd64 -v 15.0-RELEASE -a amd64 -poudriere ports -c -p default -m git -B main +# 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 Create colibri port @@ -184,11 +200,12 @@ CARGO_CRATES= ... # generated by make cargo-crates ### 2.5 Build ```sh -cd /usr/local/poudriere/ports/default/sysutils/colibri +cd /usr/local/poudriere/ports/clawdie/sysutils/colibri make makesum # generate distinfo make cargo-crates > Makefile.crates # generate crate list -poudriere bulk -j 150-amd64 -p default sysutils/colibri +# then build via the wrapper (validates + signs): +poudriere-build.sh --jail clawdie-amd64 --ports clawdie sysutils/colibri ``` ## Phase 3 - pkg repository @@ -198,7 +215,9 @@ poudriere bulk -j 150-amd64 -p default sysutils/colibri ```sh pkg install nginx # /usr/local/etc/nginx/nginx.conf: -# server { listen 80; root /usr/local/poudriere/data/packages/150-amd64-default; autoindex on; } +# 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 @@ -206,16 +225,19 @@ service nginx start ### 3.2 Client config -On ISO builds and deployed machines: +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 -# /usr/local/etc/pkg/repos/clawdie.conf -clawdie: { - url: "http://pkg.clawdie.home.arpa/", - enabled: yes, -} +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: @@ -241,7 +263,7 @@ Once the deployed-system service implementation lands and the server builds itself: ```sh -# The ml350p runs its own deployed-system clawdie service +# The mother-build host runs its own deployed-system clawdie service service clawdie enable service clawdie start @@ -254,6 +276,16 @@ The current live USB does not stage `service clawdie`; it runs ## 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 @@ -313,17 +345,18 @@ vm-bhyve ## Timeline -| Step | Effort | Depends on | -| -------------------------------------- | -------------- | ----------------------- | -| 1. Server provision (ZFS, base system) | ~1h | iLO password, ISO boots | -| 2. Poudriere setup | ~30m | base system running | -| 3. colibri port creation | ~1h | Poudriere running | -| 4. First pkg build | ~30m (compile) | port ready | -| 5. pkg repo + nginx | ~15m | packages built | -| 6. ISO integration | ~15m | repo hosted | -| 7. bhyve + test VMs | ~30m | base system + ZFS | +| 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` | -**Total: ~4h** once iLO password is available. +**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 @@ -332,9 +365,9 @@ vm-bhyve package branding. - Poudriere builds each package in a clean jail — no host Rust toolchain pollution. The jail installs `lang/rust` from ports automatically. -- 32GB RAM + tmpfs means builds stay in memory (fast, no SSD wear). +- `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. -- Xeon E5-2400 has VT-x + EPT — full hardware virt acceleration. -- bhyve guests can run concurrently with Poudriere builds (16GB reserved - for VMs, 16GB for host). +- 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). -- 2.45.3 From 95c0cf879f87f5a09ea01da84c2c0b0ac7dc3909 Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sat, 20 Jun 2026 13:47:13 +0200 Subject: [PATCH 3/4] =?UTF-8?q?docs(poudriere):=20point=20port=20section?= =?UTF-8?q?=20at=20canonical=20colibri=20port;=20fix=20MIT=E2=86=92AGPLv3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inline Makefile example claimed LICENSE=MIT (wrong — colibri is AGPL-3.0-only) and duplicated what now lives canonically in colibri/packaging/freebsd/port/. Replace the snippet with a pointer + the corrected key facts (license, binaries shipped, generated files). Co-Authored-By: Claude Opus 4.8 --- docs/POUDRIERE-BUILD-SERVER.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/docs/POUDRIERE-BUILD-SERVER.md b/docs/POUDRIERE-BUILD-SERVER.md index 9d9381cc..a30daa06 100644 --- a/docs/POUDRIERE-BUILD-SERVER.md +++ b/docs/POUDRIERE-BUILD-SERVER.md @@ -176,26 +176,17 @@ sysutils/colibri/ **Makefile** (Rust port pattern): -```makefile -PORTNAME= colibri -PORTVERSION= 0.0.1 -CATEGORIES= sysutils -MASTER_SITES= https://code.smilepowered.org/clawdie/colibri/archive/ -DISTNAME= ${PORTNAME}-${PORTVERSION} +The port files are maintained in the **colibri** repo (source-of-truth, kept +with the code) at `packaging/freebsd/port/sysutils/colibri/` — `Makefile`, +`pkg-descr`, `pkg-plist`, plus a README covering generation. Don't hand-copy a +Makefile here; drop that directory into the ports tree. Key facts: -MAINTAINER= hello@clawdie.si -COMMENT= Colibri control plane - agent supervision, skills, tasks -WWW= https://code.smilepowered.org/clawdie/colibri - -LICENSE= MIT - -USES= cargo -USE_GITHUB= no - -CARGO_CRATES= ... # generated by make cargo-crates - -.include -``` +- `LICENSE= AGPLv3` (per `colibri/Cargo.toml` — **not** MIT). +- `USES= cargo`; source from the Forgejo archive (tagged `v${DISTVERSION}`). +- Ships 6 binaries: `clawdie`, `colibri`, `colibri-daemon`, `colibri-mcp`, + `colibri-test-agent`, `colibri-tui`. +- `CARGO_CRATES` and `distinfo` are generated on the build host + (`make cargo-crates`, `make makesum`). ### 2.5 Build -- 2.45.3 From 5c5ed57abea7d5585b62c1adb63291766baf069d Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sat, 20 Jun 2026 14:59:07 +0200 Subject: [PATCH 4/4] docs(live-power): document C3 wake-safety invariant; tidy rcorder block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clawdie_live_power is a one-shot boot script that applies the power_profile C-state policy once (FreeBSD's power_profile is nostart and otherwise only runs on a devd AC-line transition). Comment-only clarification — no behavior change: - Move the PROVIDE/REQUIRE/BEFORE/KEYWORD rcorder block to the top (convention; rcorder scans the whole file, so behavior is identical). - Document scope explicitly: this selects a CPU C-state/freq profile ONLY — never suspend/sleep/blank/DPMS (screen-blank is the separate no-blank stack). - Record the wake-safety invariant: both AC (0x01) and battery (0x00) branches are safe because rc.conf pins performance_cx_lowest AND economy_cx_lowest to C3, so neither can select a deeper C-state that breaks USB resume. Guard-rail for future editors: do not deepen on the live USB. sh -n clean; rcorder tags intact. Co-Authored-By: Claude Opus 4.8 --- live/operator-session/clawdie-live-power | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/live/operator-session/clawdie-live-power b/live/operator-session/clawdie-live-power index 78c7fef4..6ecc1dc2 100644 --- a/live/operator-session/clawdie-live-power +++ b/live/operator-session/clawdie-live-power @@ -1,14 +1,22 @@ #!/bin/sh +# PROVIDE: clawdie_live_power +# REQUIRE: FILESYSTEMS syslogd +# BEFORE: LOGIN +# KEYWORD: nojail +# # Clawdie operator USB live power baseline. # Applies the rc.conf power_profile C-state policy once at boot. FreeBSD's # power_profile service is marked nostart and normally runs only when devd sees # an AC-line transition, so a live USB can otherwise sit at the firmware/default # C-state until AC is unplugged/replugged. - -# PROVIDE: clawdie_live_power -# REQUIRE: FILESYSTEMS syslogd -# BEFORE: LOGIN -# KEYWORD: nojail +# +# Scope: this selects a CPU C-state/frequency profile ONLY. It never suspends, +# sleeps, blanks the screen, or enables DPMS (screen-blank is handled separately +# by the no-blank stack: xorg.conf.d/40-clawdie-noblank.conf + the power-manager +# skel + clawdie-noblank-guard.sh). Both branches are wake-safe: rc.conf pins +# performance_cx_lowest AND economy_cx_lowest to C3, so neither the AC (0x01) nor +# battery (0x00) profile can drop into a deeper C-state that breaks the +# USB-backed resume path. Keep both at C3 on the live USB — do not deepen. . /etc/rc.subr -- 2.45.3