Skip to content

Host Operator Model

Current main uses the FreeBSD host as the operator surface.

There is no dedicated operator jail.

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 Atlas
  • tenant id: only for later additive tenants such as bob or jane

The root install is not modeled as tenant zero. It owns shared platform state.

  • SSH into the FreeBSD host
  • run Ansible against the FreeBSD host
  • manage Bastille jails from the host
  • treat the Data Service, Git Service, Web Service, and worker jails as host-managed infrastructure

This keeps the trust boundary simple and avoids a second operator layer inside the jailed runtime.

Root/shared state lives under fixed shared names:

  • system_ops
  • system_brain
  • system_skills
  • system_git
  • system_web

These belong to the shared platform regardless of the assistant display name.

The host owns:

  • Bastille lifecycle
  • warden0 bridge and PF/NAT
  • ZFS datasets and snapshots
  • rc.d service installation
  • .env
  • /etc/hosts managed internal block
  • deployment and verification steps

The service jails own:

  • PostgreSQL in the db jail when DB_RUNTIME=jail is explicitly chosen
  • the shared Git Service in the git jail
  • the shared Web Service in the cms jail (Strapi is optional, Ansible-managed when used)

Workers own only sandboxed execution.

The host uses a two-tier IP scheme:

Shared services<subnet>.x (one per host, not per assistant name):

SlotRoleNotes
<subnet>.1gatewaywarden0 host bridge
<subnet>.2Git Serviceshared git jail
<subnet>.3Web Serviceshared cms jail (nginx, Astro)
<subnet>.4Local AI Modelsshared ollama / llama.cpp runtime when enabled
<subnet>.5Data Serviceoptional db jail when DB_RUNTIME=jail

Worker range.101+ is reserved for worker and automation jails.

Set WARDEN_SUBNET_BASE in .env to the private /24 you want to use. Repo examples often use 10.0.1, but a live host may use 192.168.72 or any other private subnet. When DB_RUNTIME=host, jails connect to Postgres at ${AGENT_SUBNET_BASE}.1:5432 (warden0 on the host).

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 provide persistent services. Current policy keeps git and cms thin, keeps db thick only when the optional jail runtime is used, and keeps workers thin.

Default automation path:

operator -> ssh -> FreeBSD host -> bastille cmd -> db|git|cms|worker

That means:

  • no default sshd inside service jails
  • no default jail-to-host automation key chain
  • fewer secrets and fewer trust boundaries to maintain

At runtime, the agent user never calls sudo. All privileged host operations go through hostd — a root daemon on /var/run/clawdie-hostd.sock by default:

agent user process
-> src/hostd/client.ts -> hostd(op, params)
-> /var/run/clawdie-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:

  • clawdie_hostd_enable=YES — root daemon, always on
  • clawdie_enable=YES (or AUTO) — 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.

Two rc.d services — always separate:

ServiceUserWhy
clawdie_hostdrootNeeds bastille, ZFS, PF
clawdieclawdie service userNo privilege needed; talks to hostd via socket

The agent rc.d script uses daemon -u clawdie to drop from root to the service user before exec. The generated run-clawdie.sh wrapper sets HOME=/home/clawdie so SSH keys and git identity in the service user’s home directory resolve correctly. Runtime dirs (data/, logs/, groups/) are pre-created and chowned to the service user during setup/service.ts to avoid EACCES on first startup.

Keep operator identities explicit on the host:

  • interactive operator account, typically clawdie or atlas
  • shared platform services installed through host-managed setup steps

Avoid building operational assumptions around an internal operator jail.