Skip to main content
The WattSwarm Docker Compose stack gives you a fully wired local environment in one command. It starts four services: a PostgreSQL 16 database (the kernel’s source of truth for the event log, projections, and decision memory), the wattswarm kernel with its UI console and Iroh-backed P2P bridge, the wattswarm-runtime reference HTTP executor, and a background run worker process that continuously claims and executes queued run steps. All three Rust services use cargo watch with the local source tree bind-mounted, so any code change you save triggers an automatic rebuild and process restart — no manual docker compose restart needed.

Prerequisites

  • Docker Desktop (macOS/Windows) or Docker Engine + Compose plugin (Linux). The docker compose subcommand must be available; v2 Compose is required.
  • The WattSwarm source tree cloned locally:
    git clone https://github.com/wattetheria/wattswarm.git
    cd wattswarm
    

Default ports

ServiceHost PortNotes
PostgreSQL55432Maps to container port 5432
Kernel UI7788Kernel console at /, swarm dashboard at /swarm
Kernel P2P4001Iroh QUIC direct listen port
Runtime HTTP8787Reference runtime /health, /execute, /verify
All four ports are overridable via environment variables — see Custom ports below.

Steps

1

Clone the repo and enter the directory

If you have not already cloned the repository, do so now and change into it:
git clone https://github.com/wattetheria/wattswarm.git
cd wattswarm
The docker-compose.yml at the root of the repo defines all four services and the two named volumes (wattswarm_pg_data and wattswarm_state_data).
2

Start the full stack

Build images and start all services in detached mode:
docker compose up -d --build
Compose starts services in dependency order:
  1. postgres — waits until pg_isready passes its health check.
  2. runtime — waits until /health returns 200.
  3. kernel — waits for both postgres and runtime to be healthy, then runs run init to bootstrap the queue schema, auto-registers the rt executor pointing at http://runtime:8787, and starts the UI server on 0.0.0.0:7788.
  4. worker — starts the background run worker loop with WATTSWARM_P2P_ENABLED=false to prevent conflicts with the kernel over the shared state volume.
Because kernel, runtime, and worker bind-mount the local source tree (.:/app) and run under cargo watch, saving a .rs file automatically triggers a rebuild and clean restart of that container — no image rebuild required during normal development.
First build pulls dependencies and compiles from source. On a typical developer laptop this takes 3–5 minutes. Subsequent builds that change only application code are much faster because the layer cache preserves compiled crate artifacts.
3

Open the kernel console

Once the kernel container is healthy, open the UI console in your browser:
http://127.0.0.1:7788/
The console gives you point-and-click access to every CLI operation group: node up/down/status, peers, log head/replay/verify, executor add/list/check, task sample/submit/watch/decision/run-real, and knowledge export. It also includes a built-in Quick Start panel with ordered operation steps and a minimal TaskContract reference snippet.For the real swarm dashboard (live task lifecycle map, consensus log, and agent panels):
http://127.0.0.1:7788/swarm
The swarm dashboard drives real kernel transitions via GET /api/swarm/state and POST /api/swarm/tick, so the panels reflect actual SEL and projection state — not a simulation.
4

Submit a run via the CLI

With the compose stack running, use the CLI from your host machine and point it at the compose PostgreSQL instance with --pg-url. The --kickoff flag moves the run and its steps to QUEUED immediately after creation so the background worker can pick them up.
cargo run --bin wattswarm -- \
  run submit ./run-spec.json \
  --kickoff \
  --pg-url postgres://postgres:postgres@127.0.0.1:55432/wattswarm
The command prints a JSON object containing the new run_id. The compose worker container will begin claiming and executing run steps within WATTSWARM_WORKER_POLL_MS milliseconds (default 250 ms).
Submitting a run spec is not sufficient on its own — the run queue requires an active worker process to claim and execute steps. The compose worker service provides that worker automatically. If you run without Compose, start a worker yourself with run worker.
5

Watch the run

Poll the run status and step conclusions using the run_id returned by run submit:
cargo run --bin wattswarm -- \
  run watch <run-id> \
  --pg-url postgres://postgres:postgres@127.0.0.1:55432/wattswarm
The command prints a pretty-printed JSON view of the run record including current status (PENDING, RUNNING, FINALIZED, FAILED), per-step conclusions, and the aggregated result_json once the run reaches a terminal state. You can also stream the raw event log for a run:
cargo run --bin wattswarm -- \
  run events <run-id> \
  --pg-url postgres://postgres:postgres@127.0.0.1:55432/wattswarm

Custom ports

Override any default host port by setting environment variables before docker compose up. All four host-port variables are recognized by the compose file:
WATTSWARM_PG_HOST_PORT=56432 \
WATTSWARM_UI_PORT=8788 \
WATTSWARM_P2P_HOST_PORT=5001 \
WATTSWARM_RUNTIME_PORT=9787 \
docker compose up -d --build
VariableDefaultControls
WATTSWARM_PG_HOST_PORT55432PostgreSQL host binding
WATTSWARM_UI_PORT7788Kernel UI / console host binding
WATTSWARM_P2P_HOST_PORT4001Iroh QUIC P2P host binding
WATTSWARM_RUNTIME_PORT8787Reference runtime host binding

P2P and network mode options

CommandDescription
docker compose up -d --buildDefault — P2P enabled, Iroh-backed local mode
WATTSWARM_P2P_ENABLED=false docker compose up -d --buildLocal-only, no P2P bridge started
WATTSWARM_UDP_ANNOUNCE_ENABLED=true docker compose up -d --buildEnables UDP multicast peer discovery (default multicast group 239.255.42.99:37931)
When WATTSWARM_UDP_ANNOUNCE_ENABLED=true, the kernel emits an announce payload on startup and listens on WATTSWARM_UDP_ANNOUNCE_PORT (default 37931). Discovered peer IDs are persisted locally so the peers list command and the Peers panel in the console can surface them. To use broadcast instead of multicast:
WATTSWARM_UDP_ANNOUNCE_ENABLED=true \
WATTSWARM_UDP_ANNOUNCE_MODE=broadcast \
WATTSWARM_UDP_ANNOUNCE_ADDR=255.255.255.255 \
docker compose up -d --build

Worker tuning

The background worker container reads three environment variables that control how aggressively it claims and processes run steps:
VariableDefaultDescription
WATTSWARM_WORKER_CONCURRENCY16Maximum number of run steps claimed and executed in parallel
WATTSWARM_WORKER_POLL_MS250Milliseconds between polling cycles when no steps are available
WATTSWARM_WORKER_LEASE_MS30000Lease duration in milliseconds; a step not renewed within this window is made reclaimable
Example — tighten concurrency and use a shorter poll interval for a low-latency dev loop:
WATTSWARM_WORKER_CONCURRENCY=4 \
WATTSWARM_WORKER_POLL_MS=100 \
docker compose up -d --build
Multiple worker processes can run in parallel against the same PostgreSQL instance — step claiming is designed to be safe under concurrent access without requiring external queue middleware.

UI entrypoints

URLDescription
http://127.0.0.1:7788/Kernel console — node, peers, log, executors, tasks, knowledge
http://127.0.0.1:7788/swarmReal swarm dashboard — live lifecycle map and consensus panels
The console’s Diagnostics panel reads diagnostics/wattswarm_node.jsonl from the state directory and returns structured transport, gossip, backfill, and agent-callback events — useful for multi-node debugging without leaving the browser.

State persistence

The kernel’s node-local state is stored in the named Docker volume wattswarm_state_data, mounted at /var/lib/wattswarm inside the container. Three files drive node identity and startup behavior:
FilePurpose
startup_config.jsonNetwork mode, bootstrap contacts, gateway URLs, core agent
node_state.jsonWhether the node is currently marked up or down, and its mode
node_seed.hexEd25519 seed that determines the node’s permanent node_id
On macOS with Docker Desktop these files live inside the VM-managed volume. Access them through the container (docker exec -it wattswarm-kernel cat /var/lib/wattswarm/node_state.json) or copy them out with docker cp wattswarm-kernel:/var/lib/wattswarm/node_seed.hex . — do not rely on the repo-local .wattswarm/ directory for compose-managed state.
Deleting the wattswarm_state_data volume removes node_seed.hex. Because node_id is derived from that Ed25519 seed, your node will receive a new identity on next startup. If the same seed is later introduced into another running node, the registry bootstrap raises a conflict error to prevent duplicate identity registration.