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
|
|
|
/**
|
|
|
|
|
* setup/agent-jails.ts — Provision per-specialist agent jails.
|
|
|
|
|
*
|
|
|
|
|
* Creates db-worker, git-worker, ctrl-worker jails from infra/jails.yaml.
|
|
|
|
|
* Each gets pi + aider installed, scoped secrets, and nullfs mounts for
|
|
|
|
|
* its domain. PF rules restrict cross-jail network access.
|
|
|
|
|
*
|
2026-05-12 21:16:17 +02:00
|
|
|
* Run: just setup-agent-jails (or just install -- --from agent-jails)
|
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
|
|
|
*/
|
refactor(identity): remove PLATFORM_ID/SERVICE_NAME/RUNTIME_USER env vars
Step 5 of system-namespace cutover: complete the env-var removal that
step 4 set up. All consumers now import SERVICE_NAME from
src/platform-identity.ts directly; the deprecated PLATFORM_*
re-exports in src/config.ts are gone.
src/config.ts:
- PLATFORM_ID, PLATFORM_SERVICE_NAME, PLATFORM_RUNTIME_USER exports
removed.
- PLATFORM_RUNTIME_HOME stays (derived from SERVICE_NAME, used by
~10 consumers for path construction).
- Env-var allowlist drops PLATFORM_ID / PLATFORM_SERVICE_NAME /
PLATFORM_RUNTIME_USER / PLATFORM_RUNTIME_HOME entries.
- CONTROLPLANE_AIDER_TMUX_SESSION uses SERVICE_NAME directly.
setup/onboarding.ts:
- writeIdentity() simplified to write only ASSISTANT_NAME (display).
PLATFORM_ID / PLATFORM_SERVICE_NAME / PLATFORM_RUNTIME_USER are no
longer written to .env. Fresh installs have no PLATFORM_* keys.
- Status emission switched from PLATFORM_ID to SERVICE_NAME.
setup/env-audit.ts:
- Audit lists SERVICE_NAME instead of PLATFORM_ID; the env-file
PLATFORM_ID read is gone.
24 source files (src/*.ts, setup/*.ts, scripts/dashboard.ts):
- Bare PLATFORM_ID / PLATFORM_SERVICE_NAME / PLATFORM_RUNTIME_USER
references replaced with SERVICE_NAME.
- Imports rewired: SERVICE_NAME comes from
../{src/}platform-identity.js, not from config.js.
- Imports deduped where the sed sweep produced collisions.
Shell scripts (scripts/bhyve-evidence.sh, glass.sh, inspect-system.sh):
- Hardcoded SERVICE_NAME='clawdie' and SERVICE_USER='clawdie'.
No more grep-the-.env fallbacks; the constants are the source.
Tests (middle path):
- Mechanical fixes (import path, renamed assertion text):
src/hostd/privileged-commands.test.ts, src/startup-report.test.ts,
setup/env-audit.test.ts, setup/install-mode.test.ts.
- Skipped with `// system-namespace:` markers (pinned removed
env-driven override behavior; Codex rewrites once the bootstrap-
config service-user override path lands):
setup/verify.test.ts > 'uses the platform service name for PID candidates'
setup/service.test.ts > 'resolves a platform runtime separately from the tenant'
Test files still containing PLATFORM_* strings in vi.mock contents,
ENV_KEYS arrays, or comments are left untouched — they are test
artifacts that don't affect runtime; mock contents resolve to
'clawdie' which still equals SERVICE_NAME.
tsc clean. 2095 tests pass, 4 skipped, 0 fail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 2095 passed | 4 skipped (2099)
2026-05-02 14:49:19 +02:00
|
|
|
import { SERVICE_NAME } from '../src/platform-identity.js';
|
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
|
|
|
import fs from 'fs';
|
|
|
|
|
import path from 'path';
|
|
|
|
|
|
|
|
|
|
import {
|
2026-04-24 15:54:32 +02:00
|
|
|
AGENT_INTERNAL_DOMAIN,
|
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
|
|
|
SUBNET_BASE,
|
|
|
|
|
DB_HOST,
|
|
|
|
|
PROJECT_ROOT,
|
2026-04-23 21:41:42 +02:00
|
|
|
TENANT_ID,
|
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
|
|
|
} from '../src/config.js';
|
2026-05-03 07:20:51 +02:00
|
|
|
import { readControlplaneAgentApiKey } from '../src/controlplane-agent-keys.js';
|
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
|
|
|
import { logger } from '../src/logger.js';
|
|
|
|
|
import { readEnvFile } from '../src/env.js';
|
|
|
|
|
import { loadPackageList, mountPkgCacheInJail } from './packages.js';
|
|
|
|
|
import { commandExists, getPlatform, isRoot } from './platform.js';
|
|
|
|
|
import { emitStatus } from './status.js';
|
|
|
|
|
import {
|
|
|
|
|
bastille,
|
2026-04-16 15:47:30 +00:00
|
|
|
ensureJailExecDir,
|
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
|
|
|
jailExists,
|
|
|
|
|
detectFreeBSDRelease,
|
|
|
|
|
jailRoot,
|
|
|
|
|
resolveJailName,
|
|
|
|
|
} from './bastille-helpers.js';
|
|
|
|
|
import {
|
|
|
|
|
loadJailRegistry,
|
|
|
|
|
resolveJailIp,
|
|
|
|
|
getAgentJailDef,
|
|
|
|
|
parseMountSpec,
|
|
|
|
|
} from '../src/jail-schema.js';
|
|
|
|
|
|
|
|
|
|
const LOG = 'logs/setup.log';
|
|
|
|
|
|
|
|
|
|
const SHARED_LLM_KEYS = [
|
|
|
|
|
'ANTHROPIC_API_KEY',
|
|
|
|
|
'OPENAI_API_KEY',
|
|
|
|
|
'OPENROUTER_API_KEY',
|
|
|
|
|
'ZAI_API_KEY',
|
|
|
|
|
'GEMINI_API_KEY',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const DOMAIN_KEY_ENV_MAP: Record<string, string[]> = {
|
|
|
|
|
'db-admin': [
|
|
|
|
|
'DB_HOST',
|
|
|
|
|
'DB_PORT',
|
|
|
|
|
'SKILLS_DB_URL',
|
|
|
|
|
'MEMORY_DB_URL',
|
|
|
|
|
'OPS_DB_URL',
|
|
|
|
|
'SKILLS_DB_USER',
|
|
|
|
|
'SKILLS_DB_PASSWORD',
|
|
|
|
|
'MEMORY_DB_USER',
|
|
|
|
|
'MEMORY_DB_PASSWORD',
|
|
|
|
|
'OPS_DB_USER',
|
|
|
|
|
'OPS_DB_PASSWORD',
|
|
|
|
|
],
|
|
|
|
|
'git-admin': [
|
2026-04-18 11:47:14 +00:00
|
|
|
'GIT_LOCAL_URL',
|
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
|
|
|
'REMOTE_GIT_URL',
|
|
|
|
|
'GIT_MIRROR_URLS',
|
|
|
|
|
'GIT_SSH_KEY_PATH',
|
|
|
|
|
'FORGEJO_TOKEN',
|
2026-04-18 07:02:25 +00:00
|
|
|
'AGENT_REPO_DIR',
|
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
|
|
|
],
|
|
|
|
|
coordinator: ['CONTROLPLANE_API_KEY'],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const EXTRA_PACKAGES: Record<string, string[]> = {
|
2026-04-15 16:23:13 +00:00
|
|
|
'db-admin': ['postgresql18-client'],
|
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
|
|
|
'git-admin': [],
|
|
|
|
|
coordinator: [],
|
|
|
|
|
};
|
|
|
|
|
|
2026-04-15 16:23:13 +00:00
|
|
|
function ensurePiInstalled(jailName: string): void {
|
|
|
|
|
const exists = bastille('cmd', jailName, '/bin/sh', '-lc', 'command -v pi');
|
|
|
|
|
if (exists.ok) return;
|
|
|
|
|
|
|
|
|
|
logger.info({ jailName }, 'Installing pi inside agent jail');
|
|
|
|
|
const install = bastille(
|
|
|
|
|
'cmd',
|
|
|
|
|
jailName,
|
|
|
|
|
'npm',
|
|
|
|
|
'install',
|
|
|
|
|
'-g',
|
2026-05-09 12:35:22 +02:00
|
|
|
'@earendil-works/pi-coding-agent',
|
2026-04-15 16:23:13 +00:00
|
|
|
);
|
|
|
|
|
if (!install.ok) {
|
|
|
|
|
throw new Error(`pi install failed in ${jailName}: ${install.output}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-16 18:59:24 +00:00
|
|
|
/**
|
|
|
|
|
* Map of env var names to check and the env var name they should be written as.
|
|
|
|
|
* Pi reads keys by their provider-specific name (ZAI_API_KEY, ANTHROPIC_API_KEY, etc.)
|
|
|
|
|
*/
|
|
|
|
|
const LLM_KEY_ENTRIES: Array<{ envVar: string; writeAs: string }> = [
|
|
|
|
|
{ envVar: 'ZAI_API_KEY', writeAs: 'ZAI_API_KEY' },
|
|
|
|
|
{ envVar: 'ANTHROPIC_API_KEY', writeAs: 'ANTHROPIC_API_KEY' },
|
|
|
|
|
{ envVar: 'OPENAI_API_KEY', writeAs: 'OPENAI_API_KEY' },
|
|
|
|
|
{ envVar: 'OPENROUTER_API_KEY', writeAs: 'OPENROUTER_API_KEY' },
|
|
|
|
|
{ envVar: 'GEMINI_API_KEY', writeAs: 'GEMINI_API_KEY' },
|
|
|
|
|
];
|
|
|
|
|
|
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
|
|
|
function buildJailEnv(
|
|
|
|
|
specialist: string,
|
|
|
|
|
allEnv: Record<string, string | undefined>,
|
|
|
|
|
): string {
|
|
|
|
|
const lines: string[] = [];
|
|
|
|
|
|
2026-04-16 18:59:24 +00:00
|
|
|
// Write the first available LLM key with its correct env var name.
|
|
|
|
|
// Previously this always wrote ANTROPIC_API_KEY even for ZAI keys,
|
|
|
|
|
// causing pi to not find the key (provider mismatch).
|
|
|
|
|
for (const entry of LLM_KEY_ENTRIES) {
|
|
|
|
|
const val = allEnv[entry.envVar];
|
|
|
|
|
if (val) {
|
2026-04-18 18:54:12 +02:00
|
|
|
lines.push(`export ${entry.writeAs}=${JSON.stringify(val)}`);
|
2026-04-16 18:59:24 +00:00
|
|
|
break;
|
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
2026-04-16 18:59:24 +00:00
|
|
|
// Domain-specific keys (DB creds, git tokens, etc.)
|
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
|
|
|
const domainKeys = DOMAIN_KEY_ENV_MAP[specialist] ?? [];
|
|
|
|
|
for (const key of domainKeys) {
|
|
|
|
|
const val = allEnv[key];
|
2026-04-18 18:54:12 +02:00
|
|
|
if (val) lines.push(`export ${key}=${JSON.stringify(val)}`);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lines.join('\n') + '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function run(args: string[]): Promise<void> {
|
|
|
|
|
if (getPlatform() !== 'freebsd') {
|
|
|
|
|
emitStatus('SETUP_AGENT_JAILS', {
|
|
|
|
|
STATUS: 'failed',
|
|
|
|
|
ERROR: 'unsupported_platform',
|
|
|
|
|
LOG,
|
|
|
|
|
});
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
if (!isRoot()) {
|
|
|
|
|
emitStatus('SETUP_AGENT_JAILS', {
|
|
|
|
|
STATUS: 'failed',
|
|
|
|
|
ERROR: 'requires_root',
|
|
|
|
|
LOG,
|
|
|
|
|
});
|
|
|
|
|
throw new Error('setup_agent_jails_requires_root');
|
|
|
|
|
}
|
|
|
|
|
if (!commandExists('bastille')) {
|
|
|
|
|
emitStatus('SETUP_AGENT_JAILS', {
|
|
|
|
|
STATUS: 'failed',
|
|
|
|
|
ERROR: 'missing_bastille',
|
|
|
|
|
LOG,
|
|
|
|
|
});
|
|
|
|
|
throw new Error('missing_bastille');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const registry = loadJailRegistry();
|
|
|
|
|
const gateway = process.env.WARDEN_GATEWAY || `${SUBNET_BASE}.1`;
|
|
|
|
|
const bridge = process.env.WARDEN_BRIDGE || registry.bridge;
|
|
|
|
|
const release = detectFreeBSDRelease();
|
2026-04-24 15:54:32 +02:00
|
|
|
const internalDomain = AGENT_INTERNAL_DOMAIN;
|
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
|
|
|
const allEnv = readEnvFile([
|
|
|
|
|
...SHARED_LLM_KEYS,
|
|
|
|
|
...Object.values(DOMAIN_KEY_ENV_MAP).flat(),
|
|
|
|
|
]);
|
2026-05-03 07:20:51 +02:00
|
|
|
const coordinatorApiKey = readControlplaneAgentApiKey('coordinator');
|
|
|
|
|
if (!coordinatorApiKey) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Missing coordinator CONTROLPLANE_API_KEY. Run controlplane setup first.',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
allEnv.CONTROLPLANE_API_KEY = coordinatorApiKey;
|
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
|
|
|
|
|
|
|
|
const specialists = ['db-admin', 'git-admin', 'coordinator'];
|
|
|
|
|
const results: Record<
|
|
|
|
|
string,
|
|
|
|
|
{ jailName: string; ip: string; created: boolean }
|
|
|
|
|
> = {};
|
|
|
|
|
|
|
|
|
|
for (const specialist of specialists) {
|
|
|
|
|
const entry = getAgentJailDef(registry, specialist);
|
|
|
|
|
if (!entry) {
|
|
|
|
|
logger.warn({ specialist }, 'No agent jail definition found in registry');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { role, def } = entry;
|
2026-05-10 21:24:47 +02:00
|
|
|
const jailName = resolveJailName({ role });
|
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
|
|
|
const ip = resolveJailIp(registry, role);
|
|
|
|
|
const hostname = `${role}.${internalDomain}`;
|
|
|
|
|
|
|
|
|
|
const exists = jailExists(jailName);
|
|
|
|
|
|
|
|
|
|
if (!exists) {
|
|
|
|
|
if (args.includes('--destroy') || args.includes('--recreate')) {
|
|
|
|
|
logger.info(
|
|
|
|
|
{ jailName, specialist },
|
|
|
|
|
'Skipping creation (--destroy/--recreate passed but jail does not exist)',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
logger.info(
|
|
|
|
|
{ jailName, ip, release, specialist },
|
|
|
|
|
`Creating ${role} jail`,
|
|
|
|
|
);
|
|
|
|
|
const create = bastille(
|
|
|
|
|
'create',
|
|
|
|
|
...(def.thick ? ['-T'] : []),
|
|
|
|
|
...(def.vnet ? ['-B'] : []),
|
|
|
|
|
'-g',
|
|
|
|
|
gateway,
|
|
|
|
|
jailName,
|
|
|
|
|
release,
|
|
|
|
|
`${ip}/24`,
|
|
|
|
|
bridge,
|
|
|
|
|
);
|
|
|
|
|
if (!create.ok) {
|
|
|
|
|
throw new Error(`bastille create failed for ${role}: ${create.output}`);
|
|
|
|
|
}
|
|
|
|
|
bastille('config', jailName, 'set', 'host.hostname', hostname);
|
|
|
|
|
bastille('restart', jailName);
|
|
|
|
|
} else {
|
|
|
|
|
logger.info(
|
|
|
|
|
{ jailName, specialist },
|
|
|
|
|
`${role} jail already exists, skipping creation`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mountPkgCacheInJail(jailName);
|
|
|
|
|
|
|
|
|
|
const pkgs = loadPackageList('agent-worker-jail.txt');
|
|
|
|
|
const extra = EXTRA_PACKAGES[specialist] ?? [];
|
|
|
|
|
const allPkgs = [...pkgs, ...extra];
|
|
|
|
|
|
|
|
|
|
if (allPkgs.length > 0) {
|
|
|
|
|
const pkg = bastille('pkg', jailName, 'install', '-y', ...allPkgs);
|
|
|
|
|
if (!pkg.ok) {
|
|
|
|
|
logger.warn(
|
|
|
|
|
{ output: pkg.output, specialist },
|
|
|
|
|
`${role} jail package install had warnings`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 16:23:13 +00:00
|
|
|
ensurePiInstalled(jailName);
|
|
|
|
|
|
2026-04-16 15:47:30 +00:00
|
|
|
// Ensure jail-exec staging dir is writable by the agent user
|
refactor(identity): remove PLATFORM_ID/SERVICE_NAME/RUNTIME_USER env vars
Step 5 of system-namespace cutover: complete the env-var removal that
step 4 set up. All consumers now import SERVICE_NAME from
src/platform-identity.ts directly; the deprecated PLATFORM_*
re-exports in src/config.ts are gone.
src/config.ts:
- PLATFORM_ID, PLATFORM_SERVICE_NAME, PLATFORM_RUNTIME_USER exports
removed.
- PLATFORM_RUNTIME_HOME stays (derived from SERVICE_NAME, used by
~10 consumers for path construction).
- Env-var allowlist drops PLATFORM_ID / PLATFORM_SERVICE_NAME /
PLATFORM_RUNTIME_USER / PLATFORM_RUNTIME_HOME entries.
- CONTROLPLANE_AIDER_TMUX_SESSION uses SERVICE_NAME directly.
setup/onboarding.ts:
- writeIdentity() simplified to write only ASSISTANT_NAME (display).
PLATFORM_ID / PLATFORM_SERVICE_NAME / PLATFORM_RUNTIME_USER are no
longer written to .env. Fresh installs have no PLATFORM_* keys.
- Status emission switched from PLATFORM_ID to SERVICE_NAME.
setup/env-audit.ts:
- Audit lists SERVICE_NAME instead of PLATFORM_ID; the env-file
PLATFORM_ID read is gone.
24 source files (src/*.ts, setup/*.ts, scripts/dashboard.ts):
- Bare PLATFORM_ID / PLATFORM_SERVICE_NAME / PLATFORM_RUNTIME_USER
references replaced with SERVICE_NAME.
- Imports rewired: SERVICE_NAME comes from
../{src/}platform-identity.js, not from config.js.
- Imports deduped where the sed sweep produced collisions.
Shell scripts (scripts/bhyve-evidence.sh, glass.sh, inspect-system.sh):
- Hardcoded SERVICE_NAME='clawdie' and SERVICE_USER='clawdie'.
No more grep-the-.env fallbacks; the constants are the source.
Tests (middle path):
- Mechanical fixes (import path, renamed assertion text):
src/hostd/privileged-commands.test.ts, src/startup-report.test.ts,
setup/env-audit.test.ts, setup/install-mode.test.ts.
- Skipped with `// system-namespace:` markers (pinned removed
env-driven override behavior; Codex rewrites once the bootstrap-
config service-user override path lands):
setup/verify.test.ts > 'uses the platform service name for PID candidates'
setup/service.test.ts > 'resolves a platform runtime separately from the tenant'
Test files still containing PLATFORM_* strings in vi.mock contents,
ENV_KEYS arrays, or comments are left untouched — they are test
artifacts that don't affect runtime; mock contents resolve to
'clawdie' which still equals SERVICE_NAME.
tsc clean. 2095 tests pass, 4 skipped, 0 fail.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---
Build: pass | Tests: pass — Tests 2095 passed | 4 skipped (2099)
2026-05-02 14:49:19 +02:00
|
|
|
ensureJailExecDir(jailName, SERVICE_NAME);
|
2026-04-16 15:47:30 +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
|
|
|
const chsh = bastille(
|
|
|
|
|
'cmd',
|
|
|
|
|
jailName,
|
|
|
|
|
'chsh',
|
|
|
|
|
'-s',
|
|
|
|
|
'/usr/local/bin/bash',
|
|
|
|
|
'root',
|
|
|
|
|
);
|
|
|
|
|
if (!chsh.ok) {
|
|
|
|
|
logger.warn({ output: chsh.output }, `chsh failed in ${role}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const envContent = buildJailEnv(specialist, allEnv);
|
2026-04-18 03:57:55 +00:00
|
|
|
// Write to jail fs root (/.env.agent inside the jail) — jail-exec-runner
|
|
|
|
|
// sources this path with `set -a && . /.env.agent`.
|
|
|
|
|
const jailEnvPath = path.join(jailRoot(jailName), '.env.agent');
|
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
|
|
|
fs.mkdirSync(path.dirname(jailEnvPath), { recursive: true });
|
|
|
|
|
fs.writeFileSync(jailEnvPath, envContent);
|
|
|
|
|
logger.info(
|
|
|
|
|
{ jailName, specialist, envPath: jailEnvPath },
|
|
|
|
|
'Wrote scoped .env.agent',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (entry.agentJail.mounts && entry.agentJail.mounts.length > 0) {
|
|
|
|
|
for (const mountSpec of entry.agentJail.mounts) {
|
|
|
|
|
const mount = parseMountSpec(mountSpec);
|
|
|
|
|
const hostDir = resolveMountHostPath(mount.hostToken, allEnv);
|
|
|
|
|
if (!hostDir || !fs.existsSync(hostDir)) {
|
|
|
|
|
logger.warn(
|
|
|
|
|
{ specialist, mountHost: mount.hostToken, resolvedPath: hostDir },
|
|
|
|
|
`Skipping mount — host path not found`,
|
|
|
|
|
);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fstabPath = `/usr/local/bastille/jails/${jailName}/fstab`;
|
2026-04-18 07:02:25 +00:00
|
|
|
const jailMountTarget = `/usr/local/bastille/jails/${jailName}/root${mount.jailPath}`;
|
|
|
|
|
const desiredLine = `${hostDir} ${jailMountTarget} nullfs ${mount.mode} 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
|
|
|
|
|
|
|
|
if (fs.existsSync(fstabPath)) {
|
|
|
|
|
const existing = fs.readFileSync(fstabPath, 'utf-8');
|
2026-04-18 07:02:25 +00:00
|
|
|
const lines = existing.split('\n');
|
|
|
|
|
const idx = lines.findIndex(
|
|
|
|
|
(line) =>
|
2026-04-18 18:54:12 +02:00
|
|
|
line.includes(jailMountTarget) &&
|
|
|
|
|
line.trim().split(/\s+/u).length >= 6,
|
2026-04-18 07:02:25 +00:00
|
|
|
);
|
|
|
|
|
if (idx !== -1) {
|
|
|
|
|
if (lines[idx].trim() === desiredLine) {
|
|
|
|
|
logger.debug(
|
|
|
|
|
{ specialist, mountPoint: mount.jailPath },
|
|
|
|
|
'Mount already in fstab',
|
|
|
|
|
);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
lines[idx] = desiredLine;
|
|
|
|
|
fs.writeFileSync(fstabPath, lines.join('\n'));
|
|
|
|
|
try {
|
|
|
|
|
bastille('umount', jailName, mount.jailPath);
|
|
|
|
|
} catch {
|
|
|
|
|
// ignore — may not be mounted yet
|
|
|
|
|
}
|
2026-04-18 18:54:12 +02:00
|
|
|
bastille(
|
|
|
|
|
'mount',
|
|
|
|
|
jailName,
|
|
|
|
|
hostDir,
|
|
|
|
|
mount.jailPath,
|
|
|
|
|
'nullfs',
|
|
|
|
|
mount.mode,
|
|
|
|
|
);
|
2026-04-18 07:02:25 +00:00
|
|
|
logger.info(
|
2026-04-18 18:54:12 +02:00
|
|
|
{
|
|
|
|
|
specialist,
|
|
|
|
|
hostDir,
|
|
|
|
|
jailPoint: mount.jailPath,
|
|
|
|
|
mode: mount.mode,
|
|
|
|
|
},
|
2026-04-18 07:02:25 +00:00
|
|
|
'Updated existing mount in agent jail',
|
|
|
|
|
);
|
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
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 07:02:25 +00:00
|
|
|
const mounted = bastille(
|
|
|
|
|
'mount',
|
|
|
|
|
jailName,
|
|
|
|
|
hostDir,
|
|
|
|
|
mount.jailPath,
|
|
|
|
|
'nullfs',
|
|
|
|
|
mount.mode,
|
|
|
|
|
);
|
|
|
|
|
if (!mounted.ok) {
|
|
|
|
|
logger.warn(
|
2026-04-18 18:54:12 +02:00
|
|
|
{
|
|
|
|
|
specialist,
|
|
|
|
|
hostDir,
|
|
|
|
|
jailPoint: mount.jailPath,
|
|
|
|
|
output: mounted.output,
|
|
|
|
|
},
|
2026-04-18 07:02:25 +00:00
|
|
|
'Mount command failed in agent jail',
|
|
|
|
|
);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
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
|
|
|
logger.info(
|
|
|
|
|
{ specialist, hostDir, jailPoint: mount.jailPath, mode: mount.mode },
|
|
|
|
|
'Mounted directory into agent jail',
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results[specialist] = { jailName, ip, created: !exists };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emitStatus('SETUP_AGENT_JAILS', {
|
|
|
|
|
STATUS: 'success',
|
2026-04-14 09:39:44 +00:00
|
|
|
JAILS: JSON.stringify(results),
|
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
|
|
|
LOG,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveMountHostPath(
|
|
|
|
|
token: string,
|
|
|
|
|
env: Record<string, string | undefined>,
|
|
|
|
|
): string | null {
|
|
|
|
|
if (token === 'MIGRATIONS_DIR') {
|
|
|
|
|
return path.join(PROJECT_ROOT, 'docs', 'internal', 'sql');
|
|
|
|
|
}
|
|
|
|
|
if (token === 'SKILLS_DIR') {
|
|
|
|
|
return path.join(PROJECT_ROOT, '.agent', 'skills');
|
|
|
|
|
}
|
|
|
|
|
if (token === 'IDENTITIES_DIR') {
|
|
|
|
|
return path.join(PROJECT_ROOT, '.agent', 'identities');
|
|
|
|
|
}
|
2026-04-18 04:00:50 +00:00
|
|
|
if (token === 'PI_EXTENSIONS_DIR') {
|
|
|
|
|
return path.join(PROJECT_ROOT, '.pi', 'extensions');
|
|
|
|
|
}
|
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
|
|
|
if (token === 'REPO_DIR') {
|
2026-04-18 07:02:25 +00:00
|
|
|
const agentRepoDir = env['AGENT_REPO_DIR'];
|
|
|
|
|
if (agentRepoDir) return agentRepoDir;
|
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
|
|
|
const repoRoot = env['GIT_STORAGE_ROOT'];
|
|
|
|
|
if (repoRoot) return repoRoot;
|
|
|
|
|
return path.join(PROJECT_ROOT);
|
|
|
|
|
}
|
|
|
|
|
const envVal = env[token];
|
|
|
|
|
if (envVal && fs.existsSync(envVal)) return envVal;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2026-04-15 16:28:49 +00:00
|
|
|
|
|
|
|
|
run(process.argv.slice(2)).catch((err: unknown) => {
|
|
|
|
|
const msg = err instanceof Error ? err.message : String(err);
|
|
|
|
|
process.stderr.write(`agent-jails: ${msg}\n`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|