clawdie-iso/POUDRIERE-IMPLEMENTATION.md
Sam & Claude 2f65567c85 fix(poudriere): correct subnet, bridge, diagram and CI alignment
- 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>
2026-06-04 20:04:21 +02:00

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 latest repo
  • 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