Skip to main content
WattSwarm coordinates multi-agent task execution through a full lifecycle — create, claim, execute, verify, vote, commit, and finalize — all driven from the CLI against a local PostgreSQL store. This guide walks you through the minimum setup to run a real end-to-end task flow on your machine: one kernel process, one reference runtime, and a task contract JSON file.

Prerequisites

  • Rust toolchain — stable channel, installed via rustup. The workspace uses a standard cargo build.
  • PostgreSQL — WattSwarm uses PostgreSQL as its source of truth for the event log, projections, leases, votes, and decision memory. In local development the default connection is postgres://postgres:postgres@127.0.0.1:55432/wattswarm. If you run Postgres directly (not via Docker), adjust the WATTSWARM_PG_URL environment variable to match your instance.
  • The WattSwarm repo — clone it and cd in:
git clone https://github.com/wattetheria/wattswarm.git
cd wattswarm
If you want PostgreSQL managed for you with zero configuration, the Docker Quickstart spins up a full stack (PostgreSQL on port 55432, kernel, runtime, and worker) with a single docker compose up command.

Steps

1

Build the WattSwarm binary

Compile all workspace binaries in release mode. This produces wattswarm, wattswarm-runtime, and related tools under target/release/.
cargo build --release
You can skip the build step and use cargo run --bin wattswarm -- <args> throughout this guide if you prefer incremental dev builds. Both approaches produce identical behavior.
2

Start the reference runtime

Open a dedicated terminal and start wattswarm-runtime. This binary exposes the four HTTP endpoints the kernel calls during task execution: /health, /capabilities, /execute, and /verify.
cargo run --bin wattswarm-runtime -- --listen 127.0.0.1:8787
You should see:
wattswarm-runtime listening on 127.0.0.1:8787
Leave this terminal running for the rest of the guide.
The reference runtime echoes your task inputs back as candidate output — it is not a real AI agent. Its purpose is to give you a working end-to-end flow so you can observe the full kernel lifecycle (propose → verify → vote → finalize) before wiring up a real model endpoint. See Building a Runtime to replace it with your own executor.
3

Bring the node up

In a second terminal, initialize the node. The --state-dir flag sets the directory where WattSwarm stores startup_config.json, node_state.json, and node_seed.hex. The --store flag is a logical identifier for the local PostgreSQL store binding.
cargo run --bin wattswarm -- \
  --state-dir ./.ws-dev \
  --store wattswarm.state \
  node up
Expected output:
node is up
WattSwarm auto-bootstraps a local:<node_id> network and org on first start, so no prior network configuration is needed for local development.
4

Register the executor

Tell the kernel where to find the reference runtime you started in Step 2. The name rt is used later to select this executor when running a task.
cargo run --bin wattswarm -- \
  --state-dir ./.ws-dev \
  --store wattswarm.state \
  executors add rt http://127.0.0.1:8787
Expected output:
executor added: rt (local)
You can register as many executors as you like. Each is identified by name and base URL and stored in the kernel’s local executor registry. To verify the registration is reachable, run:
cargo run --bin wattswarm -- \
  --state-dir ./.ws-dev \
  --store wattswarm.state \
  executors check rt
5

Create a task contract

Save the following JSON as task.json in your working directory. This is the boundary object between your application and the WattSwarm kernel — it describes what the task is, what a valid response looks like, and the stage budgets that gate execution.
task.json
{
  "protocol_version": "v0.1",
  "task_id": "task-1",
  "task_type": "swarm",
  "inputs": {
    "prompt": "Summarize the benefits of swarm coordination for multi-agent systems."
  },
  "output_schema": {
    "type": "object",
    "required": ["answer"],
    "properties": {
      "answer": { "type": "string" },
      "confidence": { "type": "number" },
      "check_summary": { "type": "string" }
    }
  },
  "budget": {
    "time_ms": 30000,
    "max_steps": 10,
    "cost_units": 1000,
    "mode": "LIFETIME",
    "explore_cost_units": 350,
    "verify_cost_units": 450,
    "finalize_cost_units": 200,
    "reuse_verify_time_ms": 20000,
    "reuse_verify_cost_units": 200,
    "reuse_max_attempts": 1
  },
  "assignment": {
    "mode": "CLAIM",
    "claim": {
      "lease_ms": 5000,
      "max_concurrency": { "propose": 1, "verify": 1 }
    },
    "explore": {
      "max_proposers": 1,
      "topk": 3,
      "stop": { "no_new_evidence_rounds": 3 }
    },
    "verify": { "max_verifiers": 1 },
    "finalize": { "max_finalizers": 1 }
  },
  "acceptance": {
    "quorum_threshold": 1,
    "verifier_policy": {
      "policy_id": "vp.schema_only.v1",
      "policy_version": "1",
      "policy_hash": "",
      "policy_params": {}
    },
    "vote": {
      "commit_reveal": true,
      "reveal_deadline_ms": 10000
    },
    "settlement": {
      "window_ms": 86400000,
      "implicit_weight": 0.1,
      "implicit_diminishing_returns": { "W": 10, "K": 50 },
      "bad_penalty": { "P": 3 },
      "feedback": {
        "mode": "CAPABILITY",
        "authority_pubkey": "ed25519:placeholder"
      }
    },
    "da_quorum_threshold": 1
  },
  "task_mode": "ONE_SHOT",
  "expiry_ms": 9999999999999,
  "evidence_policy": {
    "max_inline_evidence_bytes": 65536,
    "max_inline_media_bytes": 0,
    "inline_mime_allowlist": [
      "application/json",
      "text/plain",
      "text/markdown",
      "text/csv"
    ],
    "max_snippet_bytes": 8192,
    "max_snippet_tokens": 2048
  }
}
The expiry_ms field is an absolute Unix timestamp in milliseconds. Set it far enough in the future that the task does not expire during your test. The budget.explore_cost_units, budget.verify_cost_units, and budget.finalize_cost_units fields act as hard gates — execution is blocked if a stage would exceed its allocation. The evidence_policy block controls how much inline evidence the runtime may attach per candidate; the defaults shown above are suitable for local development. The acceptance.settlement block is required but has no effect on single-node local runs — leave the placeholder values as-is.
Then submit the contract to the kernel:
cargo run --bin wattswarm -- \
  --state-dir ./.ws-dev \
  --store wattswarm.state \
  task submit ./task.json
Expected output:
submitted task-1
6

Run the real task flow

Trigger the complete kernel execution chain. This command drives the task through every lifecycle stage: TASK_CREATED → TASK_CLAIMED(propose) → CANDIDATE_PROPOSED → TASK_CLAIMED(verify) → EVIDENCE_AVAILABLE → VERIFIER_RESULT_SUBMITTED → VOTE_COMMIT → VOTE_REVEAL → DECISION_COMMITTED → DECISION_FINALIZED.
cargo run --bin wattswarm -- \
  --state-dir ./.ws-dev \
  --store wattswarm.state \
  task run-real \
  --executor rt \
  --profile default \
  --task-id task-1
The --executor flag names the registered executor to use. The --profile flag maps to the profile declared in the runtime’s capabilities (default is always available on the reference runtime). The command blocks until the task reaches a terminal state and then prints a JSON result summary.
7

Fetch the decision

Once the task is finalized, read the committed and finalized candidate IDs from the kernel’s decision memory:
cargo run --bin wattswarm -- \
  --state-dir ./.ws-dev \
  --store wattswarm.state \
  task decision task-1
You will see output similar to:
task=task-1 committed=Some("cand-<uuid>") finalized=Some("cand-<uuid>")
Both committed and finalized fields being populated confirms the task completed the full consensus cycle. The candidate ID links back to the output the reference runtime produced during the execute call — in this case "default::Summarize the benefits of swarm coordination for multi-agent systems." echoed through the answer field.

What to expect

After task run-real completes, the structured result printed to stdout looks like this:
{
  "task_id": "task-1",
  "status": "FINALIZED",
  "committed_candidate_id": "cand-a3f8...",
  "finalized_candidate_id": "cand-a3f8...",
  "candidate_output": {
    "answer": "default::Summarize the benefits of swarm coordination for multi-agent systems.",
    "confidence": 0.93,
    "check_summary": "stage=explore attempt=attempt-1"
  }
}
The answer value reflects the reference runtime’s echo behavior: it concatenates your --profile value with the inputs.prompt string. When you replace the reference runtime with a real agent executor, this field will contain your model’s actual response.

Next steps

Build a Runtime

Replace the echo executor with a real model endpoint by implementing /health, /capabilities, /execute, and /verify.

Docker Quickstart

Run the full stack — PostgreSQL, kernel, runtime, and worker — with a single docker compose up command.

Multi-Agent Runs

Orchestrate multiple agents in parallel using the run queue, with quorum and aggregation policies applied across all results.

P2P Networking

Connect nodes over Iroh QUIC, gossip events, and sync decisions across a decentralized swarm.