Run untrusted AI agents like you mean it!
Clawtilla is a Docker Compose stack setting up a self-hosted network firewall for AI agents. You run a fleet of agent containers — ClawBots — sealed inside a network cage whose only direct route out is a single gateway you control: no direct internet, no direct route to your host. The gateway is the intended egress chokepoint — connections to configured endpoints are weighed against per-endpoint allow/deny rules and logged, and when a real API key is needed the request is handed off with credentials the agent never holds and can never walk away with.
Assume every agent is already compromised, jailbroken, or just having a very bad day. The cage stops its direct escape cold and the gateway records and bounds the rest. The blast radius is contained, but not zero, and the cage is not airtight. See What's not included below.
The substance is all clawpatrol (gateway,
WireGuard, TLS inspection, policy, credential injection). Clawtilla is just a Docker Compose stack putting pieces together.
🚨 Clawtilla was not built by security professionals and has had no independent security audit. It is provided as-is, with no warranty, use it at your own risk. Read the architecture doc, threat-model it against your own needs, and decide for yourself before trusting it with anything that matters.
AI agents run untrusted code and make arbitrary network calls. This setup gives them a hard network boundary and a single auditable egress point:
- Agents are network-caged — their only route out is the gateway.
- The gateway inspects in-scope endpoints, applies allow/deny rules per host + verb, and logs everything.
- Upstream credentials stay at the gateway and are injected into requests; the agent only ever holds a placeholder token.
- Contain untrusted agents — no direct internet, no host reachability.
- A single egress chokepoint with per-endpoint policy and full request logging.
- Keep secrets out of the agent — credentials are injected at the gateway.
- Self-hostable on one machine with plain Docker Compose — no SaaS, no external dependency.
- Scale by adding agents — one service + volume per client.
- A default-deny egress allowlist — (Not Supported Yet!) Blocking arbitrary unlisted domains needs a separate egress filter.
- Not multi-host / distributed. One Docker host (designed and tested on Docker Desktop for macOS).
- Not a code-execution sandbox. It secures the network; process and kernel isolation remain the container runtime's job.
- Not hardened against a hostile co-tenant on the agent network (single-tenant assumption).
- Network-caged agent containers on an internal Docker network (no NAT).
- A userspace-WireGuard gateway — no kernel WireGuard, no extra capabilities on the gateway.
- Per-endpoint allow/deny policy, TLS inspection, and a request-log dashboard.
- Gateway-side credential injection (the agent sees only a placeholder).
- Persistent, restart-safe enrollment (no re-approval) and a decoupled agent lifecycle.
- The gateway CA auto-installed into the agent's system trust store.
- Full acceptance + security test harness.
Each is a deliberate, documented decision — see Key decisions and the design docs.
- No default-deny by domain — egress is monitor + per-endpoint policy.
- No client↔client isolation on the agent network — a compromised agent can spoof or sniff its neighbours.
- The agent runs as root with
NET_ADMINin v1 — an accepted trade-off, bounded by the cage, the VM boundary, andno-new-privileges. - Gateway → host isolation is best-effort on Docker Desktop.
- "No reach into your host" is direct-only. The cage blocks the agent's direct route off-net, but the gateway's WireGuard relay forwards agent traffic to anything the gateway itself can reach — the host-gateway, RFC1918 neighbours, or cloud metadata. No direct route, not no indirect reach.
- DNS is not contained. Tunneled DNS content isn't run through the rule engine,
and a malicious agent can query Docker's embedded resolver (
127.0.0.11) directly, bypassing the gateway — usable for low-bandwidth exfil / beaconing. - Single host only.
These and the full residual analysis are catalogued in an external network-security
review: docs/review.md.
Brief; the as-built summary is in architecture, full rationale + source citations in the archived design records.
- WireGuard, not Tailscale — Tailscale would break the cage (it needs internet to reach its control plane). → discovery
- Plain
join+ a manual whole-tunnel bring-up (Table = off), notjoin --whole-machine— the latter crash-loops on Docker's read-only/proc/sys. → discovery, plan - Agent environment supplied by a
runwrapper, not a shell rc — plain join wires no rc. → plan - Agents trust the gateway CA and the public roots — else relayed hosts (real
certs) break tools like
uv/pip. → architecture - Gateway CA installed by the entrypoint, not clawpatrol's built-in trust step
— the latter needs
sudo, whichno-new-privilegesrefuses. → discovery - Monitor + per-endpoint policy, not default-deny egress — the gateway relays unknown hosts; the cage is the boundary. → discovery
- Manual, fingerprint-verified enrollment approval — closes a plain-HTTP CA swap by a co-tenant. → plan
- Agent runs as root in v1; non-root deferred — a cost trade-off with bounded residual risk. → plan
- Gateway hardened — read-only rootfs, no added capabilities, loopback-only dashboard, named volumes (no host binds). → discovery
stack/— the deployable Compose stack (gateway + ClawBot images, policy, entrypoints).raspberry/— a bare-metal reference deployment: cloud-init seeds for flashing a Raspberry Pi fleet (gateway in Tailscale mode + ClawBot Pis behind it).tests/— acceptance + security test harness.docs/— architecture (the design record, as built) andarchive/(the historical discovery + plan, kept for rationale and source citations).- clawpatrol — the gateway source, compiled from source in the image. By
default the builder clones a pinned upstream release
(
denoland/clawpatrol@v0.5.3); overrideCLAWPATROL_REPO/CLAWPATROL_REF(or vendor aclawpatrol/checkout and setCLAWPATROL_SRC=src-local) to build a fork. Seestack/Dockerfile.
git clone <repo-url>
cd <repo>/stack
# 1. Bring up the gateway on its own — no ClawBots yet.
docker compose up -d gateway
# 2. Set the dashboard root password at http://127.0.0.1:5182 (see the warning below).
# 3. Start an agent, then approve its enrollment in the dashboard.
docker compose up -d clawbot-418
docker compose exec -it clawbot-418 run <your-agent>🚨 Set the dashboard password before starting any ClawBot. Until it is set, clawpatrol's first-run form is open to anyone who can reach
:8080— and ClawBots reach:8080over the cage by design. A ClawBot started before the password exists can claim root: the approval gate and the credential APIs.
Add agents by copying the clawbot-418 service (new name, hostname, and volume).
To run the full evaluation, see tests/README.md.
The tracked stack/ is a reference, wired for the acceptance tests
(postman-echo as the only in-scope endpoint, a dummy bearer credential). A real
deployment copies stack/ and diverges: fill gateway.hcl with your real
upstreams + credentials, add a service per agent in compose.yaml, and keep
secrets in an untracked .env. (Manage that copy however you like — e.g. vendor
stack/ and reconcile it as upstream moves.)
For bare metal rather than Docker, raspberry/ is the same idea as
cloud-init seeds: a gateway Pi in Tailscale mode plus ClawBot Pis that
clawpatrol join and tunnel through it. Copy it and diverge the same way.
Two halves you supply outside this repo:
- The agent and its provisioning. The client image ships bare — it brings
up the cage and the tunnel, then waits. You launch your agent into it
(
docker compose exec -it clawbot-418 run <agent>) and provision the agent's home however you like (e.g. bake your own dotfiles/installer into the client image). - The client half of credential injection. The gateway only swaps a
placeholder for the real secret; the agent's home must already carry that
placeholder. For ChatGPT-subscription (Codex/Hermes) auth that means seeding a
fake, far-future JWT into the agent's
auth.json— the provisioning layer's job, not this stack's. The gateway brings the upstream into scope and injects the real token. Seedocs/architecture.md.
v0 — Built against clawpatrol v0.5.3.
MIT.