- Subnet: .5 → .10 (was colliding with CMS jail at .5) - Bridge: lagg0 → warden0 (correct Bastille bridge name) - Hostname: poudriere.local → poudriere.clawdie.home.arpa (consistent with internal naming convention used by other jails) - Architecture diagram: git (.1→.4), cms (.4→.5), add full subnet legend - rsync paths: use jail filesystem path directly instead of rsync-over-SSH to the jail IP (jails share the host filesystem, no SSH hop needed) - Phase 5.2 build.sh: align with actual --fetch-only/--skip-fetch flags and explain Poudriere as a pre-fetch step in the existing pipeline - Phase 6.2: cron is fallback only — Forgejo Actions handles scheduling - Alternatives table: CI/CD marked as implemented, not rejected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
Poudriere Implementation Plan — Hybrid Package System
Status: Planning — not yet implemented Last updated: 17.mar.2026 Target: Clawdie-ISO v1.1+
Overview
Goal: Two-tier package system for Clawdie-ISO
- Base layer: Stock FreeBSD packages from official
latestrepo - Clawdie layer: Custom-built packages from Poudriere (priority: 100)
Result: USB image with offline packages that match Clawdie requirements, while still benefiting from FreeBSD security updates for base system.
Why Hybrid?
| Approach | Pros | Cons |
|---|---|---|
| Stock only | Simple, security updates from FreeBSD | Can't customize compile options, may lag on Node/Bastille versions |
| Full custom | Complete control | Long builds, must track all security updates manually |
| Hybrid | Best of both — security from base, control over Clawdie layer | Two repos to manage |
Architecture
┌─────────────────────────────────────────────────────────────┐
│ controlplane host │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ git jail │ │ db jail │ │ cms jail │ │
│ │ (.4) │ │ (.3) │ │ (.5) │ │
│ │ clawdie-iso │ │ postgres │ │ nginx │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Subnet layout: .1=gateway .2=mgmt .3=db .4=git .5=cms │
│ .6=ollama .7-.9=bhyve .10=poudriere │
│ .101+=workers │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ poudriere jail (new) │ │
│ │ (.10) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ ZFS: zroot/jails/poudriere/data │ │ │
│ │ │ - /usr/local/poudriere/data/packages │ │ │
│ │ │ - /usr/local/poudriere/data/logs │ │ │
│ │ │ - /usr/local/poudriere/jails/15amd64 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Services: │ │
│ │ - poudriere (build tool) │ │
│ │ - nginx (pkg repo server on :8080) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Build output → rsync → git jail → injected into ISO │
└─────────────────────────────────────────────────────────────┘
Phase 1: Poudriere Jail Setup
1.1 Create the jail
# On controlplane host
bastille create poudriere 15.0-RELEASE 10.0.0.10 warden0
bastille config poudriere set host.hostname poudriere.clawdie.home.arpa
bastille start poudriere
1.2 Install Poudriere
bastille pkg poudriere install poudriere dialog4ports
1.3 Configure ZFS dataset
# Create dedicated ZFS dataset for build data
zfs create -o mountpoint=/usr/local/poudriere/data zroot/poudriere
bastille config poudriere set mount.fstab += "zroot/poudriere /usr/local/poudriere/data nullfs rw 0 0"
1.4 Poudriere configuration
# /usr/local/etc/poudriere.conf (inside jail)
ZROOTFS=zroot/poudriere
FREEBSD_HOST=https://download.freebsd.org
RESOLV_CONF=/etc/resolv.conf
BASEFS=/usr/local/poudriere
USE_PORTLINT=no
USE_TMPFS=yes
NOLINUX=yes
Phase 2: Build Environment
2.1 Create build jail
# Inside poudriere jail
poudriere jail -c -j 15amd64 -v 15.0-RELEASE -a amd64
2.2 Create ports tree
poudriere ports -c -p clawdie -m git+https
2.3 Define package list
File: /usr/local/etc/poudriere.d/clawdie-pkglist
# Clawdie custom packages (built with specific options)
www/node24
sysutils/bastille
x11/desktop-installer
databases/postgresql17-client
net/rsync
devel/git
sysutils/tmux
editors/vim
shells/bash
security/sudo
# Custom Clawdie ports (to be created)
sysutils/clawdie-setup
2.4 Custom make.conf
File: /usr/local/etc/poudriere.d/15amd64-clawdie-make.conf
# Node 24 with specific options
www_node24_SET=NPM OPTIMIZED_CFLAGS
# Bastille with ZFS
sysutils_bastille_SET=ZFS
# Minimal Xorg (no VNC, no VBox)
x11_servers_xorg_server_UNSET=VNC
x11_servers_xorg_server_SET=UDEV KMS
# Desktop-installer without VirtualBox
x11_desktop-installer_UNSET=VIRTUALBOX
Phase 3: Custom Ports (Optional)
3.1 Create local ports overlay
/usr/local/poudriere/ports/clawdie/
├── sysutils/
│ └── clawdie-setup/
│ ├── Makefile
│ ├── pkg-descr
│ ├── pkg-plist
│ └── files/
│ └── setup.sh
3.2 Example Makefile for clawdie-setup
PORTNAME= clawdie-setup
PORTVERSION= 0.1.0
CATEGORIES= sysutils
MAINTAINER= hello@clawdie.si
COMMENT= Clawdie AI post-install setup wizard
RUN_DEPENDS= bash:shells/bash \
tmux:sysutils/tmux \
node:www/node24
USE_GITHUB= yes
GH_ACCOUNT= Clawdie
GH_PROJECT= Clawdie-AI
NO_BUILD= yes
do-install:
${MKDIR} ${STAGEDIR}${PREFIX}/share/clawdie
${INSTALL_SCRIPT} ${WRKSRC}/setup.sh ${STAGEDIR}${PREFIX}/share/clawdie/
.include <bsd.port.mk>
Phase 4: Build Process
4.1 Initial build
# Inside poudriere jail
poudriere bulk -j 15amd64 -p clawdie -f /usr/local/etc/poudriere.d/clawdie-pkglist
4.2 Output location
/usr/local/poudriere/data/packages/15amd64-clawdie/.latest/
├── All/
│ ├── node24-24.x.x.pkg
│ ├── bastille-0.x.x.pkg
│ └── ...
├── Latest/
│ └── (symlinks)
├── meta.txz
├── digests.txz
└── packagesite.txz
4.3 Serve via nginx
# /usr/local/etc/nginx/nginx.conf (in poudriere jail)
server {
listen 8080;
server_name localhost;
location /packages/ {
alias /usr/local/poudriere/data/packages/15amd64-clawdie/.latest/;
autoindex on;
}
}
Phase 5: ISO Integration
5.1 Sync packages to git jail
# From controlplane host — access via jail filesystem path directly (no SSH needed)
rsync -a --delete \
/usr/local/bastille/jails/poudriere/root/usr/local/poudriere/data/packages/15amd64-clawdie/.latest/ \
/home/clawdie/clawdie-iso/packages/All/
5.2 Update build.sh in clawdie-iso
The current build.sh already handles --fetch-only (stock FreeBSD packages
via pkg fetch) and --skip-fetch (use cached packages). When Poudriere is
active, add a pre-fetch step that syncs Poudriere output before the stock fetch:
# scripts/build-packages.sh syncs Poudriere output into packages/
# before ./build.sh --fetch-only runs the stock pkg fetch on top.
# pkg priority 100 for Clawdie packages ensures they win over stock.
# Poudriere output lands in packages/All/ alongside stock packages.
# pkg repo packages/ generates a single unified repo from both.
The --fetch-only stage in the Forgejo Actions pipeline should run
build-packages.sh first, then build.sh --fetch-only for stock packages.
5.3 Configure pkg priority on installed system
# In firstboot.sh, write /usr/local/etc/pkg/repos/clawdie.conf
cat > /usr/local/etc/pkg/repos/clawdie.conf << 'EOF'
Clawdie: {
url: "file:///usr/local/share/clawdie-iso/packages/clawdie",
mirror_type: "none",
enabled: yes,
priority: 100
}
FreeBSD: {
url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
mirror_type: "srv",
enabled: yes,
priority: 50
}
EOF
Phase 6: Automation
6.1 Build script
File: scripts/build-packages.sh (in clawdie-iso repo)
#!/bin/sh
set -e
echo "=== Building Clawdie packages ==="
# Trigger build in poudriere jail
jexec poudriere poudriere bulk -j 15amd64 -p clawdie -f /usr/local/etc/poudriere.d/clawdie-pkglist
# Sync to git jail
rsync -a --delete \
/usr/local/bastille/jails/poudriere/root/usr/local/poudriere/data/packages/15amd64-clawdie/.latest/ \
/home/clawdie/clawdie-iso/packages/All/
echo "=== Package build complete ==="
echo "Packages available at: /srv/git/clawdie-iso/packages/"
6.2 Scheduling
The Forgejo Actions pipeline (.forgejo/workflows/build.yml) already handles
weekly scheduling — it triggers every Sunday at 03:00 UTC and runs
build.sh --fetch-only which will call build-packages.sh first once
Poudriere is integrated (see Phase 5.2).
A cron job is only needed as a fallback if the Forgejo runner is offline:
# /etc/cron.d/clawdie-build — fallback only, Forgejo Actions is preferred
0 3 * * 0 root /home/clawdie/clawdie-iso/scripts/build-packages.sh >> /var/log/clawdie-build.log 2>&1
Phase 7: Update Flow
7.1 When Clawdie-AI changes
# 1. Update ports tree
jexec poudriere poudriere ports -u -p clawdie
# 2. Rebuild affected packages
jexec poudriere poudriere bulk -j 15amd64 -p clawdie -f /usr/local/etc/poudriere.d/clawdie-pkglist
# 3. Sync to git jail
rsync -a \
/usr/local/bastille/jails/poudriere/root/usr/local/poudriere/data/packages/15amd64-clawdie/.latest/ \
/home/clawdie/clawdie-iso/packages/All/
# 4. Rebuild ISO
cd /srv/git/clawdie-iso && ./build.sh
7.2 When FreeBSD releases security update
# Stock packages are fetched fresh on each ISO build
# Custom packages only need rebuild if they depend on updated base
Resource Requirements
| Resource | Value |
|---|---|
| Jail disk | 10 GB ZFS dataset |
| Build jail | 5 GB (15amd64 build environment) |
| Ports tree | 1 GB |
| Output packages | 500 MB - 1 GB |
| Build time (first) | 30-60 minutes |
| Build time (incremental) | 5-15 minutes |
| RAM | 2 GB recommended for build jail |
File Summary
| File | Location | Purpose |
|---|---|---|
poudriere.conf |
poudriere jail:/usr/local/etc/ | Main config |
clawdie-pkglist |
poudriere jail:/usr/local/etc/poudriere.d/ | Package list |
15amd64-clawdie-make.conf |
poudriere jail:/usr/local/etc/poudriere.d/ | Build options |
build-packages.sh |
clawdie-iso repo | Build automation |
packages/ |
git jail:/srv/git/clawdie-iso/ | Built packages |
Implementation Checklist
- Phase 1: Create poudriere jail on controlplane
- Phase 1: Install and configure Poudriere
- Phase 2: Define package list (start with ~10 packages)
- Phase 2: Create custom make.conf
- Phase 3: Create local ports overlay (optional)
- Phase 4: Run first build and verify output
- Phase 5: Integrate with build.sh in clawdie-iso
- Phase 5: Configure pkg priority on target system
- Phase 6: Create build-packages.sh automation
- Phase 7: Test ISO build with custom packages
- Phase 7: Document update procedure
Alternative Options Considered
| Option | Description | Why not chosen |
|---|---|---|
| Minimal (pre-build only) | Build ~20 custom packages, rest stock | Less control, potential ABI conflicts |
| Full custom repo | Build all ~500 packages | Long builds, must track all security updates |
| CI/CD pipeline | Automated builds on commit | Implemented — see .forgejo/workflows/build.yml and runner/README.md. Poudriere integrates into this pipeline as a pre-fetch step. |
| Poudriere-as-a-Service | Dedicated build jail serving live systems | Good for later, overkill for v1 |
| Remote build service | Outsource to cloud VM | External dependency, credential management |
References
- Poudriere Wiki
- FreeBSD Handbook: Building Packages with poudriere
- CLAWDIE-ISO.md — USB installer master plan