docs: document hostd API proxy architecture for jail agents

The hostd-bridge now routes through the controlplane API instead of
direct Unix socket. 6 files updated:

- ARCHITECTURE.md: jail isolation section — hostd via API, no socket mount
- doc/CONTROLPLANE-ARCHITECTURE.md: hostd tree shows API proxy route
- doc/CONTROLPLANE-MESSAGE-CONTRACT.md: add POST /api/controlplane/hostd
  endpoint with request/response examples
- docs/public/operate/security.md: hostd section describes HTTP proxy
  model with CONTROLPLANE_SHARED_SECRET auth
- .env.example: document CONTROLPLANE_HOST_IP (default 10.0.1.1)
- doc/HANDOFF-ISO-AGENT.md: add sections 4 (hostd API proxy) and 5
  (legacy agent ID removal) to breaking changes

Build: pass | Tests: not run (Linux) (Sam & Claude)
This commit is contained in:
Clawdie AI 2026-04-19 08:32:29 +02:00
parent cac8b4ae05
commit 8010ab3b3c
6 changed files with 49 additions and 0 deletions

View file

@ -190,6 +190,8 @@ CONTROLPLANE_NAME=clawdie
CONTROLPLANE_DAILY_TOKENS=100000
CONTROLPLANE_PORT=3100
CONTROLPLANE_BIND_HOST=0.0.0.0
# IP address that jailed agents use to reach the host API (default: 10.0.1.1).
# CONTROLPLANE_HOST_IP=10.0.1.1
CONTROLPLANE_AUTH_MODE=local_trusted
# Shared secret for agent-to-API Bearer token auth (required for agent subprocesses).
# Generate with: openssl rand -base64 32

View file

@ -77,6 +77,8 @@ Skills are injected as a compact index (~200 tokens) instead of full content (~1
When `CONTROLPLANE_JAIL_ISOLATION=YES`, specialist agents run inside dedicated thin jails. Each jail gets scoped secrets (DB creds for db-worker, SSH keys for git-worker) and restricted network access via PF. Feature flag defaults to `NO`.
Jail agents reach hostd **through the controlplane API** (`POST /api/controlplane/hostd`), not via direct Unix socket. The API authenticates the request and proxies to the hostd daemon. This means no socket mount is needed inside jails — only network access to `CONTROLPLANE_HOST_IP:CONTROLPLANE_API_PORT`.
## Split-Brain Database
All three databases run on the same PostgreSQL 18 instance, each with its own user and permissions:

View file

@ -48,6 +48,7 @@ HOST (osa)
├── hostd daemon (existing)
│ └── Privileged ops (bastille, zfs, pf) via Unix socket
│ └── API proxy: POST /api/controlplane/hostd (for jail agents)
└── PostgreSQL jail (10.0.0.3, existing)
├── clawdie_ai_* tables (existing)

View file

@ -122,6 +122,32 @@ Authorization: Bearer {CONTROLPLANE_SHARED_SECRET}
}
```
### 6. Proxy hostd Operation (jail agents)
Jail agents use this endpoint to execute privileged host operations (bastille, zfs, pf) through the controlplane API instead of direct Unix socket access.
```http
POST /api/controlplane/hostd
Authorization: Bearer {CONTROLPLANE_SHARED_SECRET}
{
"op": "bastille-list",
"params": {}
}
```
**Response:**
```json
{
"ok": true,
"output": "JID IP Address Hostname Path",
"exitCode": 0
}
```
The API proxies the request to the hostd daemon on the host. Available ops match those in `src/hostd/privileged-commands.ts` (bastille-list, bastille-cmd, zfs-snapshot, etc.).
---
## Agent Posts

View file

@ -56,6 +56,22 @@
- rc.d service uses `run-${AGENT_NAME}.sh` written by `generateRunScript()`
- ISO should run `just setup-service` which generates the correct script
### 4. hostd routed through controlplane API (cac8b4a)
- Jail agents no longer connect to hostd Unix socket directly
- `hostd-bridge.ts` rewritten as HTTP client → `POST /api/controlplane/hostd`
- API authenticates via `CONTROLPLANE_SHARED_SECRET`, proxies to hostd daemon
- No socket mount needed in jails — only network access to host IP
- New config var: `CONTROLPLANE_HOST_IP` (default `10.0.1.1`) — IP jails use to reach the API
- Jail env `CONTROLPLANE_API_URL` now uses `http://${CONTROLPLANE_HOST_IP}:${CONTROLPLANE_API_PORT}` instead of `localhost`
### 5. Legacy agent IDs removed (0f7fbc4)
- 8 agents → 5 agents (removed `sysadmin`, `db-admin`, `git-admin` alias rows)
- `resolveCanonicalAgentId()` removed — identity function since legacy IDs gone
- `CANONICAL_AGENT_MAP` deleted, `AGENT_JAIL_MAP` trimmed to canonical IDs only
- Role constraint in schema updated (removed legacy role values)
## What still works unchanged
- `.pi/extensions/clawdie-harness/skill-tools.ts` — self-contained, reads `/opt/skills` in jails

View file

@ -60,6 +60,8 @@ At runtime, privileged host operations from the agent go through `hostd`
The agent user calls `hostd(op, params)`; the daemon validates params with Zod
and runs the handler as root. Unknown ops and invalid params are rejected.
**Jail agents call hostd through the controlplane API** (`POST /api/controlplane/hostd`), not via direct socket. The extension's `hostd-bridge.ts` sends an HTTP request to the API, which authenticates via `CONTROLPLANE_SHARED_SECRET` and proxies to the hostd daemon. This eliminates the need to mount the Unix socket into jails — only network access to the host IP is required.
Shell injection prevention: `hostd` applies Zod regex validators on all
string parameters in `src/privileged-commands.ts``jailName`, `datasetName`,
`snapshotTag`, and `serviceName` must match strict patterns before any command