clawdie-ai/docs/internal/BROWSER-JAIL-FREEBSD-VIABILITY.md
Operator & Codex 6c549e7ad0 Rename browser validation assets
---
Build: pass | Tests: pass — 2383 passed (175 files)
2026-05-11 17:32:22 +02:00

4.6 KiB

Browser Jail FreeBSD Viability

Date: 11.maj.2026 Status: PASS — headless Chromium + CDP works in a Bastille VNET jail

Summary

Phase 0.5 passed on the FreeBSD host. A fresh throwaway Bastille VNET jail was able to install system-pkg Chromium, launch it in headless mode with a remote debugging port, and accept a puppeteer-core CDP connection from Node inside the jail.

The committed vision-validation renderer (scripts/browser-jail-validation/render.mjs) was also copied into the jail and successfully re-rendered all deterministic fixtures through the same CDP path.

Environment

  • Host: FreeBSD 15.0-RELEASE-p8 amd64
  • Bastille release used for jail creation: 15.0-RELEASE
  • Jail reported release after creation: 15.0-RELEASE-p4
  • Bridge: warden0
  • Gateway: 192.168.72.1
  • Validation jail requested name: browser-validation
  • Actual validation jail name: browservalidation
  • Validation jail IP: 192.168.72.150/24

browser-validation could not be used as a VNET jail name because Bastille rejected hyphens for VNET jail names:

[ERROR]: VNET jail names may not contain (-|_) characters.

The production jail name browser is unaffected by this limitation. Future validation docs should use browservalidation if VNET parity is desired.

Package versions

Installed in the throwaway jail:

chromium-147.0.7727.101
node22-22.22.2
npm-node22-11.11.0
puppeteer-core@24.43.0

Commands that worked

Create the throwaway VNET jail:

sudo bastille create -B -g 192.168.72.1 browservalidation 15.0-RELEASE 192.168.72.150/24 warden0

Install packages:

sudo bastille pkg browservalidation install -y chromium node22 npm-node22

Create the Chromium profile directory:

sudo bastille cmd browservalidation mkdir -p /var/tmp/validation-profile

Start headless Chromium under daemon:

sudo bastille cmd browservalidation daemon -f -p /var/run/browser-validation-chromium.pid \
  /usr/local/bin/chrome \
  --headless=new \
  --no-sandbox \
  --disable-gpu \
  --remote-debugging-address=127.0.0.1 \
  --remote-debugging-port=9222 \
  --user-data-dir=/var/tmp/validation-profile \
  about:blank

Verify CDP is reachable from inside the jail:

sudo bastille cmd browservalidation fetch -qo - http://127.0.0.1:9222/json/version

Observed response:

{
  "Browser": "Chrome/147.0.7727.101",
  "Protocol-Version": "1.3",
  "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/147.0.0.0 Safari/537.36",
  "V8-Version": "14.7.173.19",
  "WebKit-Version": "537.36 (@56536d2a8034c51b0e68e1a0483ab9f1a0165ae3)",
  "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/..."
}

Install the CDP client in the jail workspace:

sudo bastille cmd browservalidation sh -c 'cd /var/tmp/browser-jail-validation && npm install'

Run the smoke script:

sudo bastille cmd browservalidation sh -c 'cd /var/tmp/browser-jail-validation && node freebsd-cdp-smoke.mjs'

Observed output:

OK text=hello screenshot=/var/tmp/browser-jail-validation/hello.png

Run the deterministic renderer through the jail's Chromium:

sudo bastille cmd browservalidation sh -c 'cd /var/tmp/browser-jail-validation && node render.mjs'

Observed output:

[01-login] /var/tmp/browser-jail-validation/screenshots/01-login.png  (5 targets)
[02-dashboard] /var/tmp/browser-jail-validation/screenshots/02-dashboard.png  (7 targets)
[03-modal] /var/tmp/browser-jail-validation/screenshots/03-modal.png  (5 targets)
done

Working smoke script

The script is committed at:

scripts/browser-jail-validation/freebsd-cdp-smoke.mjs

It connects to http://127.0.0.1:9222, opens a page, navigates to a data URL, reads <h1> text through page.evaluate, writes a PNG screenshot, prints OK, and disconnects.

FreeBSD-specific notes

  • The Chromium executable from the FreeBSD package is /usr/local/bin/chrome, not chromium.
  • --no-sandbox was required for this validation launch shape. This matches the handoff's validation-only allowance. Production should re-test sandboxed launch during Phase 1 jail-service hardening.
  • --disable-gpu was used to keep headless launch minimal in the jail.
  • puppeteer-core works against system Chromium; no bundled browser download was involved.
  • npm emitted Unknown global config "python"; it did not block install or execution.

Gate decision

Phase 0.5 is green. Phase 1 can proceed using:

  • system-pkg Chromium from FreeBSD ports,
  • Node 22,
  • puppeteer-core over CDP,
  • a jail-side HTTP service that owns the Chromium process lifecycle.

chrome-remote-interface fallback was not needed.