- mission_runtime::spawn_sweeper: force-removes runtime containers for missions terminal for >=30 min, clears runtime_endpoint. Wired into clawmates-server main(). - docker-compose socket-proxy: NETWORKS=1 so bollard.connect_network can attach containers to clawmates_edge for provider egress. - phase_runner ordering: ensure_checkout BEFORE ensure_container so the mission dir exists before docker mounts it. - provisioner: mkdir_p the mission dir defensively for research-only missions that skip checkout entirely.
165 lines
5.8 KiB
YAML
165 lines
5.8 KiB
YAML
# Air-gapped single-node topology. The network segmentation IS the security
|
|
# model (spec §15):
|
|
# edge — host-published entry points only (frontend, server API)
|
|
# core — internal: server <-> postgres
|
|
# sandbox_net — internal, NO egress: agent sandboxes (joined in P2)
|
|
# secrets_net — internal: secret broker + server only (joined in P2)
|
|
# Images are preloaded from the signed offline bundle; nothing pulls at
|
|
# install time.
|
|
|
|
name: clawmates
|
|
|
|
networks:
|
|
edge: {}
|
|
core:
|
|
internal: true
|
|
sandbox_net:
|
|
internal: true
|
|
secrets_net:
|
|
internal: true
|
|
# Server <-> socket proxy only; the raw Docker socket never reaches the
|
|
# server container.
|
|
engine_net:
|
|
internal: true
|
|
|
|
volumes:
|
|
pgdata: {}
|
|
# Unix socket + master key shared ONLY between server and broker —
|
|
# the unix-socket equivalent of the K8s sidecar topology.
|
|
broker_run: {}
|
|
broker_key: {}
|
|
# Per-mission repo checkouts + generated PDFs live on the host at
|
|
# /var/lib/clawmates-missions so the (separately-managed)
|
|
# clawmates-runtime container can bind-mount the SAME host path and
|
|
# see the same tree the server wrote to. A named docker volume
|
|
# would work too but would need volume-name knowledge in the
|
|
# runtime's spawn command.
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: clawmates
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set in .env}
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
networks: [core]
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U postgres -d clawmates"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 12
|
|
|
|
# Named volumes mount root-owned, but the broker runs as uid 10001 from
|
|
# a scratch image (no shell to chown itself). This one-shot prepares the
|
|
# socket + key volumes, then exits. `on-failure` so a transient chown error
|
|
# retries instead of wedging boot (broker/server depend on it completing).
|
|
volume-init:
|
|
image: busybox:1.36
|
|
command: ["sh", "-c", "chown -R 10001:10001 /run/clawmates /etc/clawmates-broker"]
|
|
user: "0:0"
|
|
volumes:
|
|
- broker_run:/run/clawmates
|
|
- broker_key:/etc/clawmates-broker
|
|
restart: on-failure:5
|
|
|
|
# The secret broker: separate process, separate image; credentials
|
|
# never leave it. Reachable only via the shared unix socket volume.
|
|
broker:
|
|
image: clawmates/broker:${CLAWMATES_VERSION:-latest}
|
|
build:
|
|
context: ../..
|
|
dockerfile: images/broker.Dockerfile
|
|
restart: unless-stopped
|
|
environment:
|
|
CLAWMATES_BROKER_SOCKET: /run/clawmates/broker.sock
|
|
CLAWMATES_BROKER_KEY_FILE: /etc/clawmates-broker/broker.key
|
|
CLAWMATES_DATABASE__URL: postgres://postgres:${POSTGRES_PASSWORD:?set in .env}@postgres:5432/clawmates
|
|
volumes:
|
|
- broker_run:/run/clawmates
|
|
- broker_key:/etc/clawmates-broker
|
|
# `core` stays for postgres access; `edge` grants OUTBOUND internet so the
|
|
# broker can act as the §14 door (fetch Gitea/GitHub/GitLab repo lists,
|
|
# forward Slack sends, etc.) without leaking any credential back to
|
|
# cm-api. The broker still doesn't accept inbound TCP — it only listens
|
|
# on the unix socket inside broker_run.
|
|
networks: [core, edge]
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
volume-init:
|
|
condition: service_completed_successfully
|
|
|
|
# Allow-listed Docker API (§15 blast-radius cap): the server can
|
|
# create/exec/stop/remove sandbox containers and NOTHING else — no
|
|
# image builds, no networks, no secrets, no volumes. Proven by
|
|
# crates/cm-sandbox/tests/socket_proxy.rs against this exact allowlist.
|
|
socket-proxy:
|
|
image: tecnativa/docker-socket-proxy:0.3
|
|
restart: unless-stopped
|
|
environment:
|
|
CONTAINERS: 1
|
|
POST: 1
|
|
EXEC: 1
|
|
DELETE: 1
|
|
VERSION: 1
|
|
# NETWORKS grant (C3): mission_runtime::ensure_container needs
|
|
# to attach per-mission runtime containers to the clawmates_edge
|
|
# network for provider egress, in addition to creating them on
|
|
# clawmates_core.
|
|
NETWORKS: 1
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
networks: [engine_net]
|
|
|
|
server:
|
|
image: clawmates/server:${CLAWMATES_VERSION:-latest}
|
|
build:
|
|
context: ../..
|
|
dockerfile: images/server.Dockerfile
|
|
restart: unless-stopped
|
|
# Operator knobs (bootstrap owner, LLM provider/key overrides, OTLP) come
|
|
# from .env — only the keys you actually set are injected, so unset
|
|
# options stay absent rather than overriding clawmates.toml with "".
|
|
env_file:
|
|
- path: .env
|
|
required: false
|
|
environment:
|
|
CLAWMATES_CONFIG: /etc/clawmates/clawmates.toml
|
|
CLAWMATES_DATABASE__URL: postgres://postgres:${POSTGRES_PASSWORD:?set in .env}@postgres:5432/clawmates
|
|
DOCKER_HOST: tcp://socket-proxy:2375
|
|
CLAWMATES_MISSIONS_ROOT: /var/lib/clawmates-missions
|
|
CLAWMATES_RUNTIME_CONTAINER: ${CLAWMATES_RUNTIME_CONTAINER:-clawmates-runtime}
|
|
volumes:
|
|
- ./clawmates.toml:/etc/clawmates/clawmates.toml:ro
|
|
- broker_run:/run/clawmates
|
|
# Per-mission repo checkouts. Bind-mounted host path so the
|
|
# separately-managed clawmates-runtime container can see the
|
|
# same trees at the same path when it exec's for scans/benches.
|
|
- /var/lib/clawmates-missions:/var/lib/clawmates-missions
|
|
networks: [edge, core, engine_net]
|
|
ports:
|
|
- "8080:8080"
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
|
|
frontend:
|
|
image: clawmates/frontend:${CLAWMATES_VERSION:-latest}
|
|
build:
|
|
context: ../..
|
|
dockerfile: images/frontend.Dockerfile
|
|
restart: unless-stopped
|
|
# AUTH_MODE + CLERK_* (when running Clerk) come from .env; see docs/clerk.md.
|
|
env_file:
|
|
- path: .env
|
|
required: false
|
|
environment:
|
|
API_ORIGIN: http://server:8080
|
|
networks: [edge]
|
|
ports:
|
|
- "3000:3000"
|
|
depends_on:
|
|
- server
|