Host Operator Model
Current main uses the FreeBSD host as the operator surface.
There is no dedicated operator jail.
Identity split
Section titled “Identity split”Keep these roles separate:
- operator account: the human login account on the host, for example
sam - service account: the account that runs Clawdie, default
clawdie - shared platform namespace:
system - assistant display name: for example
Mevy - tenant id: only for later additive tenants such as
boborjane
The root install is not modeled as tenant zero. It owns shared platform state.
Core Rule
Section titled “Core Rule”- SSH into the FreeBSD host
- run Ansible against the FreeBSD host
- manage Bastille jails from the host
- treat
{agent}-db,{agent}-git, and{agent}-cmsas host-managed service jails
This keeps the trust boundary simple and avoids a second operator layer inside the jailed runtime.
Shared platform resources
Section titled “Shared platform resources”Root/shared state lives under fixed shared names:
system_opssystem_brainsystem_skillssystem_gitsystem_web
These belong to the shared platform regardless of the assistant display name.
Responsibilities
Section titled “Responsibilities”The host owns:
- Bastille lifecycle
warden0bridge and PF/NAT- ZFS datasets and snapshots
- rc.d service installation
.env/etc/hostsmanaged internal block- deployment and verification steps
The service jails own:
- PostgreSQL in
{agent}-db - the shared Git Service in
{agent}-git - the shared Web Service in
{agent}-cms(Strapi is optional, Ansible-managed when used)
Workers own only sandboxed execution.
Subnet Layout
Section titled “Subnet Layout”The host uses a two-tier IP scheme:
Shared services — 10.0.0.x (one per host, not per agent):
| Slot | Role | Notes |
|---|---|---|
10.0.0.1 | gateway | warden0 host bridge |
10.0.0.2 | Git Service | shared git jail |
10.0.0.3 | Web Service | shared cms jail (nginx, Astro) |
10.0.0.4 | Local AI Models | shared ollama / llama.cpp runtime when enabled |
10.0.0.5 | Data Service | optional db jail when DB_RUNTIME=jail |
Per-agent subnets — 10.0.N.x where N is the agent index (1, 2, …):
| Slot | Role | Notes |
|---|---|---|
.1 | gateway | warden0 |
.211 | db worker | Phase 7 db worker jail |
.212 | git worker | Phase 7 git worker jail |
.213 | ctrl worker | Phase 7 ctrl worker jail |
Set WARDEN_SUBNET_BASE=10.0.N in .env to assign a per-agent subnet. When DB_RUNTIME=host, jails connect to Postgres at 10.0.N.1:5432 (warden0 on the host).
Control vs Observe
Section titled “Control vs Observe”The host controls — hostd, controlplane, watchdog all run on the host with root access. They issue commands to jails via bastille, ZFS, PF, and rc.d.
Service jails ({agent}-db, {agent}-git, {agent}-cms) provide persistent services. Worker jails provide isolated execution sandboxes.
Access Pattern
Section titled “Access Pattern”Default automation path:
operator -> ssh -> FreeBSD host -> bastille cmd -> db|git|cms|workerThat means:
- no default
sshdinside service jails - no default jail-to-host automation key chain
- fewer secrets and fewer trust boundaries to maintain
Privileged Host Daemon
Section titled “Privileged Host Daemon”At runtime, the agent user never calls sudo. All privileged host operations
go through hostd — a root daemon on /var/run/<agent>-hostd.sock:
agent user process -> src/hostd/client.ts -> hostd(op, params) -> /var/run/<agent>-hostd.sock (Unix socket, mode 0660, group clawdie) -> src/hostd/daemon.ts (root) -> whitelisted op handler (bastille, zfs, pf, service, etc.)Two rc.conf entries:
{agent}_hostd_enable=YES— root daemon, always on{agent}_enable=YES(orAUTO) — user agent
The --step hostd setup step installs the rc.d script and starts the daemon.
src/controlplane.ts checks hostd reachability at startup and every 5 minutes,
and attempts to repair service jails and PF via hostd when needed.
Privilege Model
Section titled “Privilege Model”Two rc.d services per agent — always separate:
| Service | User | Why |
|---|---|---|
{agent}_hostd | root | Needs bastille, ZFS, PF |
{agent} | {agent} user | No privilege needed; talks to hostd via socket |
The agent rc.d script uses daemon -u {agentName} to drop from root to the agent user before exec. The run-{agent}.sh wrapper sets HOME=/home/{agentName} so SSH keys and git identity in the user’s home directory resolve correctly. Runtime dirs (data/, logs/, groups/) are pre-created and chowned to the agent user during setup/service.ts to avoid EACCES on first startup.
Identities
Section titled “Identities”Keep operator identities explicit on the host:
- interactive operator account, typically
clawdieormevy - shared platform services installed through host-managed setup steps
Avoid building operational assumptions around an internal operator jail.