2026-03-15 14:37:33 +00:00
|
|
|
import { execSync } from 'child_process';
|
2026-03-14 15:26:46 +01:00
|
|
|
import fs from 'fs';
|
|
|
|
|
import path from 'path';
|
|
|
|
|
import { fileURLToPath } from 'url';
|
|
|
|
|
|
2026-04-04 15:40:47 +00:00
|
|
|
import { readEnvFile } from '../src/env.js';
|
|
|
|
|
|
2026-03-14 15:26:46 +01:00
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
|
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
|
const PACKAGE_DIR = path.resolve(__dirname, '../infra/packages');
|
|
|
|
|
|
|
|
|
|
export type PackageListName =
|
|
|
|
|
| 'host-baseline.txt'
|
|
|
|
|
| 'worker-jail.txt'
|
feat(phase7a): per-specialist agent jail isolation — registry, provisioner, exec runner
Phase 7 of the Agent Harness Evolution Plan. Each non-sysadmin specialist
(db-admin, git-admin, coordinator) gets its own Bastille jail with scoped
secrets and network isolation. Sysadmin stays on host (needs bastille/PF/ZFS).
New files:
- setup/agent-jails.ts — provisions 3 agent jails from infra/jails.yaml
- src/jail-exec-runner.ts — runs pi/aider inside named jails via bastille exec
- infra/packages/agent-worker-jail.txt — shared package list
- infra/pf-agent-jails.conf — PF network isolation rules
- doc/PHASE7-AGENT-JAILS-HANDOFF.md — FreeBSD agent handoff tasks
Modified:
- infra/jails.yaml — db-worker (211), git-worker (212), ctrl-worker (213)
- src/jail-schema.ts — AgentJailSchema, parseMountSpec, parseNetworkAllow
- src/config.ts — DB_WORKER_IP, GIT_WORKER_IP, CTRL_WORKER_IP
- setup/install.ts — agent-jails step after controlplane
- setup/packages.ts — agent-worker-jail package list type
- .agent/identities/*.md — jail boundary docs
Jail topology:
- db-worker (10.0.0.211) → db jail :5432, host :3100
- git-worker (10.0.0.212) → git jail :22/:3000, internet :443
- ctrl-worker (10.0.0.213) → host :3100 only
Sub-phases 7b-7e (routing, secret verification, tests) are in the handoff doc.
Co-Authored-By: Claude (Linux) <noreply@anthropic.com>
---
Build: pass | Tests: 1019 passed (Linux — 4 FreeBSD-only tests skipped)
---
2026-04-14 08:23:40 +02:00
|
|
|
| 'agent-worker-jail.txt'
|
2026-03-15 15:47:54 +00:00
|
|
|
| 'git-jail.txt'
|
2026-04-02 08:20:07 +00:00
|
|
|
| 'forgejo-jail.txt'
|
2026-03-14 15:26:46 +01:00
|
|
|
| 'cms-jail.txt'
|
feat(infra): full subnet layout — named slots, VM profiles, ollama step
- jail-config.ts: SUBNET_SLOT constant (.1–.10), expand AGENT_JAIL_PROFILES
Record to include ollama, management, cnc, freebsdDesktop, debian, windows;
update validateJailConfig role list; fix browserVm IP to SUBNET_SLOT.freebsdDesktop
- setup/install.ts: reorder steps (worker→git→db→skills-memory→cms→browser-vm→ollama),
use service names in labels, add ollama step (FEATURE_OLLAMA-gated)
- setup/packages.ts: add ollama-jail.txt, management-jail.txt, cnc-jail.txt to
PackageListName type and loadAllPackageLists
- infra/packages: add ollama-jail.txt, management-jail.txt, cnc-jail.txt stubs
- HOST-OPERATOR-MODEL.md: subnet layout table, control-vs-observe section
- install.html: updated IP table with all named slots and bhyve VM entries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 414 passed | 10 skipped (424)
2026-03-16 05:06:24 +00:00
|
|
|
| 'db-jail.txt'
|
2026-05-11 16:38:49 +02:00
|
|
|
| 'browser-jail.txt'
|
feat(infra): full subnet layout — named slots, VM profiles, ollama step
- jail-config.ts: SUBNET_SLOT constant (.1–.10), expand AGENT_JAIL_PROFILES
Record to include ollama, management, cnc, freebsdDesktop, debian, windows;
update validateJailConfig role list; fix browserVm IP to SUBNET_SLOT.freebsdDesktop
- setup/install.ts: reorder steps (worker→git→db→skills-memory→cms→browser-vm→ollama),
use service names in labels, add ollama step (FEATURE_OLLAMA-gated)
- setup/packages.ts: add ollama-jail.txt, management-jail.txt, cnc-jail.txt to
PackageListName type and loadAllPackageLists
- infra/packages: add ollama-jail.txt, management-jail.txt, cnc-jail.txt stubs
- HOST-OPERATOR-MODEL.md: subnet layout table, control-vs-observe section
- install.html: updated IP table with all named slots and bhyve VM entries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 414 passed | 10 skipped (424)
2026-03-16 05:06:24 +00:00
|
|
|
| 'ollama-jail.txt'
|
2026-04-02 15:18:22 +00:00
|
|
|
| 'llama-cpp-jail.txt'
|
feat(infra): full subnet layout — named slots, VM profiles, ollama step
- jail-config.ts: SUBNET_SLOT constant (.1–.10), expand AGENT_JAIL_PROFILES
Record to include ollama, management, cnc, freebsdDesktop, debian, windows;
update validateJailConfig role list; fix browserVm IP to SUBNET_SLOT.freebsdDesktop
- setup/install.ts: reorder steps (worker→git→db→skills-memory→cms→browser-vm→ollama),
use service names in labels, add ollama step (FEATURE_OLLAMA-gated)
- setup/packages.ts: add ollama-jail.txt, management-jail.txt, cnc-jail.txt to
PackageListName type and loadAllPackageLists
- infra/packages: add ollama-jail.txt, management-jail.txt, cnc-jail.txt stubs
- HOST-OPERATOR-MODEL.md: subnet layout table, control-vs-observe section
- install.html: updated IP table with all named slots and bhyve VM entries
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 414 passed | 10 skipped (424)
2026-03-16 05:06:24 +00:00
|
|
|
| 'management-jail.txt'
|
|
|
|
|
| 'cnc-jail.txt';
|
2026-03-14 15:26:46 +01:00
|
|
|
|
|
|
|
|
export function loadPackageList(name: PackageListName): string[] {
|
|
|
|
|
const file = path.join(PACKAGE_DIR, name);
|
|
|
|
|
const content = fs.readFileSync(file, 'utf-8');
|
|
|
|
|
return content
|
|
|
|
|
.split('\n')
|
|
|
|
|
.map((line) => line.trim())
|
|
|
|
|
.filter((line) => line.length > 0 && !line.startsWith('#'));
|
|
|
|
|
}
|
2026-03-15 12:36:40 +00:00
|
|
|
|
2026-03-15 14:37:33 +00:00
|
|
|
/**
|
2026-04-15 16:23:13 +00:00
|
|
|
* Nullfs-mount the shared host pkg cache into a jail.
|
2026-03-15 14:37:33 +00:00
|
|
|
* Idempotent — checks the jail fstab before adding the mount.
|
|
|
|
|
* Silently skips if the cache dataset or bastille fstab don't exist yet.
|
|
|
|
|
* Call this after jail creation and before any bastille pkg install.
|
|
|
|
|
*/
|
|
|
|
|
export function mountPkgCacheInJail(jailName: string): void {
|
|
|
|
|
const fstabPath = `/usr/local/bastille/jails/${jailName}/fstab`;
|
|
|
|
|
if (!fs.existsSync('/var/cache/pkg')) return;
|
2026-04-15 16:23:13 +00:00
|
|
|
|
|
|
|
|
const desiredLine = `/var/cache/pkg /usr/local/bastille/jails/${jailName}/root/var/cache/pkg nullfs rw 0 0`;
|
|
|
|
|
if (fs.existsSync(fstabPath)) {
|
|
|
|
|
const existing = fs.readFileSync(fstabPath, 'utf-8');
|
|
|
|
|
if (existing.includes(desiredLine)) return;
|
|
|
|
|
|
|
|
|
|
if (existing.includes('/var/cache/pkg') && existing.includes(' nullfs ro ')) {
|
|
|
|
|
const updated = existing.replace(
|
|
|
|
|
/^\/var\/cache\/pkg\s+.*?\/var\/cache\/pkg\s+nullfs\s+ro\s+0\s+0\s*$/gmu,
|
|
|
|
|
desiredLine,
|
|
|
|
|
);
|
|
|
|
|
fs.writeFileSync(fstabPath, updated);
|
|
|
|
|
try {
|
|
|
|
|
execSync(`bastille umount -a ${jailName} /var/cache/pkg`, { stdio: 'ignore' });
|
|
|
|
|
} catch {
|
|
|
|
|
// ignore — may not be mounted yet
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
execSync(`bastille mount ${jailName} /var/cache/pkg /var/cache/pkg nullfs rw 0 0`, {
|
|
|
|
|
stdio: 'ignore',
|
|
|
|
|
});
|
|
|
|
|
} catch {
|
|
|
|
|
// mount failed — jail pkg installs will fall back to network downloads
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (existing.includes('/var/cache/pkg')) return;
|
2026-03-15 14:37:33 +00:00
|
|
|
}
|
2026-04-15 16:23:13 +00:00
|
|
|
|
2026-03-15 14:37:33 +00:00
|
|
|
try {
|
feat(phase7a): per-specialist agent jail isolation — registry, provisioner, exec runner
Phase 7 of the Agent Harness Evolution Plan. Each non-sysadmin specialist
(db-admin, git-admin, coordinator) gets its own Bastille jail with scoped
secrets and network isolation. Sysadmin stays on host (needs bastille/PF/ZFS).
New files:
- setup/agent-jails.ts — provisions 3 agent jails from infra/jails.yaml
- src/jail-exec-runner.ts — runs pi/aider inside named jails via bastille exec
- infra/packages/agent-worker-jail.txt — shared package list
- infra/pf-agent-jails.conf — PF network isolation rules
- doc/PHASE7-AGENT-JAILS-HANDOFF.md — FreeBSD agent handoff tasks
Modified:
- infra/jails.yaml — db-worker (211), git-worker (212), ctrl-worker (213)
- src/jail-schema.ts — AgentJailSchema, parseMountSpec, parseNetworkAllow
- src/config.ts — DB_WORKER_IP, GIT_WORKER_IP, CTRL_WORKER_IP
- setup/install.ts — agent-jails step after controlplane
- setup/packages.ts — agent-worker-jail package list type
- .agent/identities/*.md — jail boundary docs
Jail topology:
- db-worker (10.0.0.211) → db jail :5432, host :3100
- git-worker (10.0.0.212) → git jail :22/:3000, internet :443
- ctrl-worker (10.0.0.213) → host :3100 only
Sub-phases 7b-7e (routing, secret verification, tests) are in the handoff doc.
Co-Authored-By: Claude (Linux) <noreply@anthropic.com>
---
Build: pass | Tests: 1019 passed (Linux — 4 FreeBSD-only tests skipped)
---
2026-04-14 08:23:40 +02:00
|
|
|
execSync(
|
2026-04-15 16:23:13 +00:00
|
|
|
`bastille mount ${jailName} /var/cache/pkg /var/cache/pkg nullfs rw 0 0`,
|
feat(phase7a): per-specialist agent jail isolation — registry, provisioner, exec runner
Phase 7 of the Agent Harness Evolution Plan. Each non-sysadmin specialist
(db-admin, git-admin, coordinator) gets its own Bastille jail with scoped
secrets and network isolation. Sysadmin stays on host (needs bastille/PF/ZFS).
New files:
- setup/agent-jails.ts — provisions 3 agent jails from infra/jails.yaml
- src/jail-exec-runner.ts — runs pi/aider inside named jails via bastille exec
- infra/packages/agent-worker-jail.txt — shared package list
- infra/pf-agent-jails.conf — PF network isolation rules
- doc/PHASE7-AGENT-JAILS-HANDOFF.md — FreeBSD agent handoff tasks
Modified:
- infra/jails.yaml — db-worker (211), git-worker (212), ctrl-worker (213)
- src/jail-schema.ts — AgentJailSchema, parseMountSpec, parseNetworkAllow
- src/config.ts — DB_WORKER_IP, GIT_WORKER_IP, CTRL_WORKER_IP
- setup/install.ts — agent-jails step after controlplane
- setup/packages.ts — agent-worker-jail package list type
- .agent/identities/*.md — jail boundary docs
Jail topology:
- db-worker (10.0.0.211) → db jail :5432, host :3100
- git-worker (10.0.0.212) → git jail :22/:3000, internet :443
- ctrl-worker (10.0.0.213) → host :3100 only
Sub-phases 7b-7e (routing, secret verification, tests) are in the handoff doc.
Co-Authored-By: Claude (Linux) <noreply@anthropic.com>
---
Build: pass | Tests: 1019 passed (Linux — 4 FreeBSD-only tests skipped)
---
2026-04-14 08:23:40 +02:00
|
|
|
{
|
|
|
|
|
stdio: 'ignore',
|
|
|
|
|
},
|
|
|
|
|
);
|
2026-03-15 14:37:33 +00:00
|
|
|
} catch {
|
|
|
|
|
// mount failed — jail pkg installs will fall back to network downloads
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 16:38:49 +02:00
|
|
|
/**
|
|
|
|
|
* Remove the pkg-cache nullfs mount from a jail and clean any matching fstab
|
|
|
|
|
* entry. Safe to call even if the mount was never added.
|
|
|
|
|
*/
|
|
|
|
|
export function unmountPkgCacheInJail(jailName: string): void {
|
|
|
|
|
const fstabPath = `/usr/local/bastille/jails/${jailName}/fstab`;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
execSync(`bastille umount ${jailName} /var/cache/pkg`, { stdio: 'ignore' });
|
|
|
|
|
} catch {
|
|
|
|
|
// ignore — may already be unmounted or absent
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(fstabPath)) return;
|
|
|
|
|
|
|
|
|
|
const existing = fs.readFileSync(fstabPath, 'utf-8');
|
|
|
|
|
const updated = existing.replace(
|
|
|
|
|
/^\/var\/cache\/pkg\s+.*?\/var\/cache\/pkg\s+nullfs\s+(?:ro|rw)\s+0\s+0\s*$/gmu,
|
|
|
|
|
'',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (updated !== existing) {
|
|
|
|
|
fs.writeFileSync(fstabPath, updated.replace(/\n{3,}/g, '\n\n'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-15 12:36:40 +00:00
|
|
|
/**
|
|
|
|
|
* Load and deduplicate all package lists (host + all jail roles).
|
|
|
|
|
* This is the authoritative set for prefetching to the shared pkg cache.
|
|
|
|
|
*/
|
|
|
|
|
export function loadAllPackageLists(): string[] {
|
2026-04-04 15:40:47 +00:00
|
|
|
const envValues = readEnvFile(['FEATURE_TAILSCALE']);
|
|
|
|
|
const tailscaleEnabled = /^(YES|yes|true|TRUE|1)$/u.test(
|
|
|
|
|
process.env.FEATURE_TAILSCALE || envValues.FEATURE_TAILSCALE || '',
|
|
|
|
|
);
|
2026-03-15 12:36:40 +00:00
|
|
|
const all = (
|
feat(wayland): Wayland-first display stack, bhyve defaults, no feature flags
Package lists:
- host-baseline: add Wayland (seatd, weston, cage, wayvnc, waypipe, xwayland)
and bhyve (vm-bhyve, grub2-bhyve, uefi-edk2-bhyve) — all default, no flags
- worker-jail: add cage + chromium — workers own the display layer for agents
- management-jail: add bash, curl, sqlite3 baseline
environment.ts:
- HOST_PREREQUISITE_CHECKS: add entries for all 9 new host packages
- getPrereq/logger/emitStatus wired for SEATD, WESTON, CAGE, WAYVNC, WAYPIPE,
XWAYLAND, VM_BHYVE, GRUB_BHYVE, UEFI_BHYVE
install.ts:
- Remove FEATURE_OLLAMA and FEATURE_BHYVE_GUI skipUnlessEnv gates
- Add management step (.2 observability jail)
- Add debian-vm step (.8) and windows-vm step (.9, skipUnlessFile for ISO)
- bhyve VMs provision by default — no user decision required
packages.ts: include ollama-jail and management-jail in prefetch list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 414 passed | 10 skipped (424)
2026-03-16 05:40:21 +00:00
|
|
|
[
|
feat(phase7a): per-specialist agent jail isolation — registry, provisioner, exec runner
Phase 7 of the Agent Harness Evolution Plan. Each non-sysadmin specialist
(db-admin, git-admin, coordinator) gets its own Bastille jail with scoped
secrets and network isolation. Sysadmin stays on host (needs bastille/PF/ZFS).
New files:
- setup/agent-jails.ts — provisions 3 agent jails from infra/jails.yaml
- src/jail-exec-runner.ts — runs pi/aider inside named jails via bastille exec
- infra/packages/agent-worker-jail.txt — shared package list
- infra/pf-agent-jails.conf — PF network isolation rules
- doc/PHASE7-AGENT-JAILS-HANDOFF.md — FreeBSD agent handoff tasks
Modified:
- infra/jails.yaml — db-worker (211), git-worker (212), ctrl-worker (213)
- src/jail-schema.ts — AgentJailSchema, parseMountSpec, parseNetworkAllow
- src/config.ts — DB_WORKER_IP, GIT_WORKER_IP, CTRL_WORKER_IP
- setup/install.ts — agent-jails step after controlplane
- setup/packages.ts — agent-worker-jail package list type
- .agent/identities/*.md — jail boundary docs
Jail topology:
- db-worker (10.0.0.211) → db jail :5432, host :3100
- git-worker (10.0.0.212) → git jail :22/:3000, internet :443
- ctrl-worker (10.0.0.213) → host :3100 only
Sub-phases 7b-7e (routing, secret verification, tests) are in the handoff doc.
Co-Authored-By: Claude (Linux) <noreply@anthropic.com>
---
Build: pass | Tests: 1019 passed (Linux — 4 FreeBSD-only tests skipped)
---
2026-04-14 08:23:40 +02:00
|
|
|
'host-baseline.txt',
|
|
|
|
|
'worker-jail.txt',
|
|
|
|
|
'agent-worker-jail.txt',
|
|
|
|
|
'git-jail.txt',
|
|
|
|
|
'forgejo-jail.txt',
|
|
|
|
|
'cms-jail.txt',
|
|
|
|
|
'db-jail.txt',
|
2026-05-11 16:38:49 +02:00
|
|
|
'browser-jail.txt',
|
feat(phase7a): per-specialist agent jail isolation — registry, provisioner, exec runner
Phase 7 of the Agent Harness Evolution Plan. Each non-sysadmin specialist
(db-admin, git-admin, coordinator) gets its own Bastille jail with scoped
secrets and network isolation. Sysadmin stays on host (needs bastille/PF/ZFS).
New files:
- setup/agent-jails.ts — provisions 3 agent jails from infra/jails.yaml
- src/jail-exec-runner.ts — runs pi/aider inside named jails via bastille exec
- infra/packages/agent-worker-jail.txt — shared package list
- infra/pf-agent-jails.conf — PF network isolation rules
- doc/PHASE7-AGENT-JAILS-HANDOFF.md — FreeBSD agent handoff tasks
Modified:
- infra/jails.yaml — db-worker (211), git-worker (212), ctrl-worker (213)
- src/jail-schema.ts — AgentJailSchema, parseMountSpec, parseNetworkAllow
- src/config.ts — DB_WORKER_IP, GIT_WORKER_IP, CTRL_WORKER_IP
- setup/install.ts — agent-jails step after controlplane
- setup/packages.ts — agent-worker-jail package list type
- .agent/identities/*.md — jail boundary docs
Jail topology:
- db-worker (10.0.0.211) → db jail :5432, host :3100
- git-worker (10.0.0.212) → git jail :22/:3000, internet :443
- ctrl-worker (10.0.0.213) → host :3100 only
Sub-phases 7b-7e (routing, secret verification, tests) are in the handoff doc.
Co-Authored-By: Claude (Linux) <noreply@anthropic.com>
---
Build: pass | Tests: 1019 passed (Linux — 4 FreeBSD-only tests skipped)
---
2026-04-14 08:23:40 +02:00
|
|
|
'ollama-jail.txt',
|
|
|
|
|
'llama-cpp-jail.txt',
|
|
|
|
|
'management-jail.txt',
|
|
|
|
|
'cnc-jail.txt',
|
feat(wayland): Wayland-first display stack, bhyve defaults, no feature flags
Package lists:
- host-baseline: add Wayland (seatd, weston, cage, wayvnc, waypipe, xwayland)
and bhyve (vm-bhyve, grub2-bhyve, uefi-edk2-bhyve) — all default, no flags
- worker-jail: add cage + chromium — workers own the display layer for agents
- management-jail: add bash, curl, sqlite3 baseline
environment.ts:
- HOST_PREREQUISITE_CHECKS: add entries for all 9 new host packages
- getPrereq/logger/emitStatus wired for SEATD, WESTON, CAGE, WAYVNC, WAYPIPE,
XWAYLAND, VM_BHYVE, GRUB_BHYVE, UEFI_BHYVE
install.ts:
- Remove FEATURE_OLLAMA and FEATURE_BHYVE_GUI skipUnlessEnv gates
- Add management step (.2 observability jail)
- Add debian-vm step (.8) and windows-vm step (.9, skipUnlessFile for ISO)
- bhyve VMs provision by default — no user decision required
packages.ts: include ollama-jail and management-jail in prefetch list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 414 passed | 10 skipped (424)
2026-03-16 05:40:21 +00:00
|
|
|
] as PackageListName[]
|
2026-03-15 12:36:40 +00:00
|
|
|
).flatMap(loadPackageList);
|
2026-04-04 15:40:47 +00:00
|
|
|
if (tailscaleEnabled) {
|
|
|
|
|
all.push('tailscale');
|
|
|
|
|
}
|
2026-03-15 12:36:40 +00:00
|
|
|
return [...new Set(all)];
|
|
|
|
|
}
|