Files
clawmates/docs/agent-engine-architecture.md
T
Omar SobhandClaude Opus 4.8 bd982943b8
ci / gates (push) Has been cancelled
ci / rust (push) Has been cancelled
ci / sandbox-k8s (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / e2e (push) Has been cancelled
chore: commit ZeroClaw per-tenant runtime spike + architecture doc
Saves earlier-phase artifacts that were sitting untracked:
- docs/agent-engine-architecture.md — per-tenant containerized ZeroClaw runtime
  decision doc (clawmates = §15 control plane; zeroclaw = per-tenant runtime).
- deploy/clawmates-runtime/ — slim runtime Dockerfile, dev compose, example
  agent config, README (the proven Phase-1 drive recipe).
- tools/runtime-spike/drive.mjs — Node WS drive client for the spike.

No secrets (only env-var names / commented placeholders).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-15 21:29:16 -07:00

213 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent Engine Architecture — per-tenant containerized runtime
_Status: accepted direction (Phase 0). Supersedes the assumption that Clawmates' own
`cm-runtime` is the long-term execution engine. This document is the source of truth for the
backend agent-execution strategy; implementation proceeds in the phases at the end._
## Problem
Clawmates must let each customer build their own multi-agent "company/team" and run it at
scale: many agents, across machines, with full lifecycle management (provision → run →
suspend → resume → retire) — without weakening the §15 safety contract (per-tool human
approval, taint, secret broker, single audited journal) that is the product's moat and is
acceptance-blocking.
Clawmates' current `cm-runtime` (`crates/cm-runtime/src/runtime.rs`) is a real agent loop, but
it is single-host: one server process drives all agents. It does not scale horizontally or
isolate tenants at the infrastructure layer.
## Options considered
| Engine | Verdict |
|---|---|
| **clawverse** (Rust, distributed mission orchestrator) | Closest to "distributed agent fleet," but mission/repo-shaped, partly WIP, and re-implements node scheduling/allocation/health that Kubernetes already provides. **Shelved** — revisit only if one tenant must span many nodes/GPUs, or for its mission/self-improvement IP. |
| **openclaw** (Node agent OS) | Most complete *single-host* agent OS, but Node (our stack is Rust), single-tenant by design, and we lack confirmed source + license to fork/ship it. **Not the base.** |
| **zeroclaw** (`~/projects/zeroclaw`, Rust, MIT/Apache) | Multi-agent-per-daemon runtime; ownable, single-binary, embeddable, ~80 providers, 6-layer sandbox, gateway/RPC drive API. **Chosen as the per-tenant runtime.** |
## Decision
**Run one containerized ZeroClaw daemon per workspace/tenant.** The unit of scaling becomes the
*tenant container*, placed and lifecycle-managed by a standard orchestrator (Kubernetes or Fly
Machines) — which replaces a bespoke node/allocation layer. Clawmates remains the **control
plane**: tenancy, auth, billing, the audited SSE gateway, and the **§15 safety engine**. The
tenant's agents reach the outside world **only** through Clawmates-gated capabilities.
ZeroClaw **v0.8.0** makes this viable nearly out of the box:
- **Multi-agent per daemon** — isolated workspace/memory/model/policy/persona per agent → one
container = one tenant's whole team.
- **RPC transport** (local socket / remote WSS + token) with **restart-surviving, tmux-style
sessions** → clean drive channel and clean scale-to-zero/resume.
- **Schema-V3 config CRUD** over the gateway (`/api/config/*`) → declarative provisioning.
- **Per-agent tool allowlists + MCP client + private-host allowlists** → the hook to make
Clawmates the only outbound door.
- **Attribution-aware structured logs** + **per-agent/per-model cost tracking** → feed the
audited journal and billing.
- **Lean channel bundle as Cargo features** (`crates/zeroclaw-channels/Cargo.toml`:
`default-channels = acp-server, email, telegram, webhook`) → tiny, fast-cold-start image.
- **MIT/Apache license** → we may fork, containerize, and ship it.
## Existing infrastructure & what we reuse (this is already proven here)
The per-tenant ZeroClaw-in-a-container model is **already running in production** in this fleet
(ClawBooks, EasyA, gr33t). Clawmates reuses that proven stack rather than introducing K8s/Fly:
- **Substrate:** Docker + Nginx/Traefik + a per-app **orchestrator** on Hetzner VMs (Tailscale mesh).
The orchestrator (`:3500`) provisions/deprovisions a container per user; a webhook bridge (`:3501`)
reacts to Clerk `user.created`; Nginx routes `/ws/agent/{userId}` → the container.
- **Per-user container:** ZeroClaw binary + a small domain sidecar + plugins/skills.
- **Per-user volume:** `/data/users/{id}/` = `config.toml`, `domain.db` (SQLite), `brain.h5`
(ZeroClaw's HDF5/EdgeHDF5 memory) , `workspace/`, `channels/`. Backup = **2 portable files**
(`domain.db` + `brain.h5`) rsynced offsite — trivial tenant move/restore.
- **Image registry:** private registry at `web-01:5000` (Tailscale-only).
- **Auth + billing:** Clerk + Stripe (Clawmates already supports Clerk auth mode).
- **Bootstrap scripts (reuse):** `redclawsystems/zeroclaw → deploy/clawbooks/bootstrap/`
(`scale-up.sh` = Hetzner provision + bootstrap + deploy in ~5 min; `bootstrap-node.sh`;
`deploy-to-node.sh`).
- **Secrets:** Infisical at `icarus.lan:8443` (`HETZNER_API_TOKEN_RW/RO`, `ANTHROPIC/OPENAI/…`,
Clerk, Stripe, Cloudflare, Tailscale). ⚠️ Some are still plaintext in the vault and flagged for
rotation — rotate before production.
### Resources / deploy target (live check)
The 6-server Hetzner fleet (Helsinki) has ample headroom; **no new server is needed**. Dev/test
target: **gw-05** (8c / 15Gi, ~188G free, lightly loaded) — or gw-01 (~234G free). Estimated
capacity ~50100 concurrent per-tenant containers across the fleet without new hardware.
## Target architecture
```
┌──────────────────────────────────────────────────────────────────────┐
│ Clawmates control plane (Rust SaaS) — responsibilities UNCHANGED │
│ • tenancy, auth, billing, product UI, the single audited SSE gateway │
│ • §15 engine: approvals · taint · gated categories · single-use │
│ grants · secret broker (cm-safety, cm-secrets, cm-api) │
│ • Clawmates MCP server: the ONLY outbound door (email/slack/pay/…) │
│ • tenant-runtime manager: provision/suspend/resume one container/tenant│
└───────────────┬────────────────────────────────────────────────────────┘
│ (a) provision agents via /api/config
│ (b) run turns via sessions/prompt (SSE) or RPC
│ (c) pull cost + logs
│ (d) agent's only egress = our gated MCP tools
┌──────────────────────────────────────────────────────────────────────┐
│ Per-tenant ZeroClaw daemon (slim container, our fork) │
│ • N named agents (= N clawmates): per-agent workspace/memory/model │
│ • NO native outbound channels/secrets; tools.allow locked │
│ • MCP client → Clawmates MCP server for every sensitive capability │
└───────────────┬────────────────────────────────────────────────────────┘
│ spawns sandboxes for shell/browser
Per-agent sandboxes — sibling pods / gVisor, brokered via socket-proxy
┌──────────────────────────────────────────────────────────────────────┐
│ Substrate: Hetzner VMs (Tailscale) · Docker · Nginx/Traefik · │
│ per-app orchestrator (:3500) + Clerk webhook bridge (:3501) │
│ — the EXISTING proven ClawBooks/EasyA stack (replaces clawverse). │
│ K8s/Fly only if scale later demands it. │
└──────────────────────────────────────────────────────────────────────┘
```
## Concept mapping
| Clawmates | ZeroClaw |
|---|---|
| workspace / tenant | one daemon (one container) |
| claw (agent) | one named agent (isolated workspace/memory/model/policy) |
| chat session / turn | `POST /api/sessions/new` + `POST /api/session/{id}/prompt` (SSE), or an RPC session |
| the claw's "computer" / files | `/api/agents/{alias}/workspace/*` + a sibling sandbox for shell/browser |
| routine | a zeroclaw cron job bound to that agent |
| credits / billing | `/api/cost` (per-agent / per-model splits, cached-input tokens) |
| audit journal (`run_events`) | gateway attribution-aware logs, mirrored into `run_events` |
| §15 outbound action | a **Clawmates MCP tool** the agent calls — we gate + broker + journal |
## §15 — the safety contract stays in Clawmates
Two independent layers:
1. **No native edges.** Each tenant agent is provisioned with `tools.allow` excluding native
outbound/dangerous tools and with **no real channel or secret config**. It physically cannot
email, post, pay, or exfiltrate on its own.
2. **One gated door.** Clawmates exposes an **MCP server** of the sensitive capabilities (send
email, post to Slack, move money, delete data, change access, grant infra access). When an
agent invokes one:
- the call lands in Clawmates → classify `Effect` + `TaintSource` (reuse `cm-tools` policy),
- if gated → create an `approvals` row, render a preview, **suspend**, await a human decision,
- on approve → consume the single-use `execution_grants` row, **broker-execute with the
secret** (`cm-secrets`, never exposing the credential), and journal to `run_events`.
This reuses the existing approval state machine (`cm-safety`), secret broker (`cm-secrets`),
and the audited gateway (`cm-api`) verbatim. Defense-in-depth: run agents at ZeroClaw's
**Supervised** risk profile so its own per-dispatch allowlist + `ask_operator` back-stop us.
The §15 properties (taint-by-default, gated categories, single audited channel, single-use
grants, broker isolation) are therefore **unchanged** — they move from gating `cm-runtime`'s
in-process tools to gating the MCP boundary.
## Slim per-tenant image
- Fork/vendor zeroclaw; build with only needed features
(`--no-default-features --features channel-webhook` + MCP client), no native chat channels.
Push to the private registry at `web-01:5000`.
- Gateway bound to an in-pod socket / WSS with a per-tenant token; Nginx routes
`/ws/agent/{userId}` → the container (existing convention).
- Per-tenant **persistent volume** at `/data/users/{id}/` (`config.toml`, `domain.db`,
`brain.h5`, `workspace/`) — the proven 2-file (`domain.db` + `brain.h5`) backup/restore applies.
- Provider keys injected at start via env (`ZEROCLAW_*`) from Infisical / the Clawmates broker,
not baked into the image.
## Risks / open questions
- **Idle economics** — a container per user is costly; the existing orchestrator can **stop idle
containers** and start them on next request (per-tenant volume persists; v0.8 restart-surviving
sessions make resume clean). Tune idle TTL / wake latency; K8s+KEDA or Fly auto-stop only if we
outgrow the orchestrator.
- **Nested sandboxing** — tool sandboxes inside a tenant container; prefer **sibling** sandbox
pods via the existing `socket-proxy` pattern over privileged DinD; evaluate gVisor/sysbox.
- **§15 coverage** — must guarantee *no* un-gated path to the outside; audit zeroclaw's native
tool set per build and lock `tools.allow`.
- **Fork maintenance** — zeroclaw is v0.8 **beta**, workspace `publish=false`; maintain a thin
fork/vendor and track upstream.
- **Provisioning contract** — Schema-V3 config CRUD is the provisioning API; pin its behavior.
## Phased delivery
- **Phase 0 — this document.** Decision + architecture + §15-via-MCP + risks. ✅
- **Phase 1 — Spike (on gw-05).** Build a slim zeroclaw container (push to `web-01:5000`); from a
small Clawmates-side client, provision one agent via `/api/config`, run one turn via
`sessions/prompt`, stream events back, and prove a §15 gate by routing one outbound action
through a stub Clawmates MCP tool. Reuse the ClawBooks `deploy/clawbooks/bootstrap/` scripts as
the starting point.
- **Phase 2 — Provisioning + lifecycle.** Adapt the existing orchestrator (`:3500`) + Clerk
webhook (`:3501`) + Nginx pattern to provision/stop/resume a Clawmates tenant container +
per-claw agent on workspace/claw create.
- **Phase 3 — §15 MCP server + journal/cost bridge.** Real gated MCP capabilities (`cm-safety` /
`cm-secrets`); map zeroclaw attribution logs + `/api/cost` into `run_events` / billing.
- **Phase 4 — Idle stop/resume + per-agent sandboxes (sibling containers via socket-proxy).**
- **Phase 5 — Cutover** from `cm-runtime` to the containerized runtime behind a feature flag.
> Reuse source-of-truth: `redclawsystems/zeroclaw → deploy/clawbooks/bootstrap/` and the
> ClawBooks/EasyA orchestrator. Rotate the plaintext Hetzner/Cloudflare/Clerk tokens (vault
> `Infrastructure Overview.md`) into Infisical before production.
## Verification
- Phase 1: end-to-end on a dev box — one claw answers a turn on a containerized zeroclaw via
Clawmates, events journaled, one approval gate fires through the Clawmates MCP door.
- Keep Clawmates' E2E + §15 suites (`p0``p7`, approval/secret-broker tests) green against the
new backend behind the flag before cutover.
- Confirm slim-image cold-start and resume-from-sleep preserve sessions.
## References
- Clawmates runtime + safety: `crates/cm-runtime/src/runtime.rs`, `crates/cm-safety/`,
`crates/cm-secrets/`, `crates/cm-api/`, `crates/cm-tools/`, `migrations/0001_init.sql`,
spec `docs/spec.md` §15.
- ZeroClaw: `~/projects/zeroclaw``crates/zeroclaw-channels/Cargo.toml` (channel features),
`crates/zeroclaw-gateway/src` (drive API: config / sessions / workspace / cost / cron),
multi-agent runtime + RPC transport (v0.8.0).
- Infra & proven pattern (Obsidian vault `~/projects/Valhalla`): `20 Infrastructure/Infrastructure
Overview.md`, `20 Infrastructure/Tailscale Network Map.md`, `20 Infrastructure/10 Architecture/
SaaS Platform Template.md`, `20 Infrastructure/30 Runbooks/Credentials Inventory.md`.
- Reuse: `redclawsystems/zeroclaw → deploy/clawbooks/bootstrap/`; private registry `web-01:5000`;
Infisical `icarus.lan:8443`; dev/test box `gw-05`.