> ## Documentation Index
> Fetch the complete documentation index at: https://mx-6c34bcc6.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# WattSwarm with Docker Compose: PostgreSQL to Kernel

> Spin up PostgreSQL, the WattSwarm kernel, reference runtime, and worker with hot reload using Docker Compose — no manual Rust setup required.

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:

  ```bash theme={null}
  git clone https://github.com/wattetheria/wattswarm.git
  cd wattswarm
  ```

## Default ports

| Service      | Host Port | Notes                                              |
| ------------ | --------- | -------------------------------------------------- |
| PostgreSQL   | `55432`   | Maps to container port `5432`                      |
| Kernel UI    | `7788`    | Kernel console at `/`, swarm dashboard at `/swarm` |
| Kernel P2P   | `4001`    | Iroh QUIC direct listen port                       |
| Runtime HTTP | `8787`    | Reference runtime `/health`, `/execute`, `/verify` |

All four ports are overridable via environment variables — see [Custom ports](#custom-ports) below.

## Steps

<Steps>
  <Step title="Clone the repo and enter the directory">
    If you have not already cloned the repository, do so now and change into it:

    ```bash theme={null}
    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`).
  </Step>

  <Step title="Start the full stack">
    Build images and start all services in detached mode:

    ```bash theme={null}
    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.

    <Tip>
      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.
    </Tip>
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.

    ```bash theme={null}
    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`).

    <Note>
      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`.
    </Note>
  </Step>

  <Step title="Watch the run">
    Poll the run status and step conclusions using the `run_id` returned by `run submit`:

    ```bash theme={null}
    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:

    ```bash theme={null}
    cargo run --bin wattswarm -- \
      run events <run-id> \
      --pg-url postgres://postgres:postgres@127.0.0.1:55432/wattswarm
    ```
  </Step>
</Steps>

## 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:

```bash theme={null}
WATTSWARM_PG_HOST_PORT=56432 \
WATTSWARM_UI_PORT=8788 \
WATTSWARM_P2P_HOST_PORT=5001 \
WATTSWARM_RUNTIME_PORT=9787 \
docker compose up -d --build
```

| Variable                  | Default | Controls                         |
| ------------------------- | ------- | -------------------------------- |
| `WATTSWARM_PG_HOST_PORT`  | `55432` | PostgreSQL host binding          |
| `WATTSWARM_UI_PORT`       | `7788`  | Kernel UI / console host binding |
| `WATTSWARM_P2P_HOST_PORT` | `4001`  | Iroh QUIC P2P host binding       |
| `WATTSWARM_RUNTIME_PORT`  | `8787`  | Reference runtime host binding   |

## P2P and network mode options

| Command                                                            | Description                                                                          |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `docker compose up -d --build`                                     | Default — P2P enabled, Iroh-backed local mode                                        |
| `WATTSWARM_P2P_ENABLED=false docker compose up -d --build`         | Local-only, no P2P bridge started                                                    |
| `WATTSWARM_UDP_ANNOUNCE_ENABLED=true docker compose up -d --build` | Enables 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:

```bash theme={null}
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:

| Variable                       | Default | Description                                                                               |
| ------------------------------ | ------- | ----------------------------------------------------------------------------------------- |
| `WATTSWARM_WORKER_CONCURRENCY` | `16`    | Maximum number of run steps claimed and executed in parallel                              |
| `WATTSWARM_WORKER_POLL_MS`     | `250`   | Milliseconds between polling cycles when no steps are available                           |
| `WATTSWARM_WORKER_LEASE_MS`    | `30000` | Lease 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:

```bash theme={null}
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

| URL                           | Description                                                    |
| ----------------------------- | -------------------------------------------------------------- |
| `http://127.0.0.1:7788/`      | Kernel console — node, peers, log, executors, tasks, knowledge |
| `http://127.0.0.1:7788/swarm` | Real 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

<Note>
  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:

  | File                  | Purpose                                                       |
  | --------------------- | ------------------------------------------------------------- |
  | `startup_config.json` | Network mode, bootstrap contacts, gateway URLs, core agent    |
  | `node_state.json`     | Whether the node is currently marked up or down, and its mode |
  | `node_seed.hex`       | Ed25519 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.
</Note>

<Warning>
  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.
</Warning>
