Skip to content

Run a fleet worker

Run cc_worker on a spare machine to add execution capacity to your fleet. It is a pure-Dart binary — no Flutter engine — that pairs with a cc_server, declares the host’s capabilities, heartbeats, pulls leased jobs, executes them and streams process events back. It holds no durable state: a supervisor restarts it and it re-registers.

You need a running cc_server first — see Run a headless server.

  • The Dart SDK. This repo pins its SDK with fvm, so prefix commands with fvm.
  • This repository checked out on the worker machine.
  • A reachable cc_server (loopback for dev, LAN or remote for production).
  • git on the PATH — the worker materializes repos and runs git probes.
Terminal window
cd apps/cc_worker
fvm dart build cli

This produces a self-contained bundle at ./build/cli/<os_arch>/bundle/bin/cc_worker.

A production server only accepts a worker that presents a paired-device id and pre-shared key. Mint them on the server machine, while no server is holding the data directory:

Terminal window
cc_server pair --data-dir <dir> --device my-worker --label "Build box"

It prints a Device id and a Pairing key — those are what --device-id and --psk take. (Omit the credential only against a loopback dev server.)

Against a paired server:

Terminal window
./build/cli/<os_arch>/bundle/bin/cc_worker \
--server wss://host:9030 --device-id my-worker --psk <psk>

Against a loopback dev server with no auth handshake:

Terminal window
fvm dart run cc_worker --server ws://localhost:9030
Flag Description Default
--server <url> cc_server URL (ws:///wss://; http(s) and a missing /rpc path are coerced). Required.
--name <name> Operator-facing worker name. host name
--device-id <id> Stable paired-device id, also used as the worker id. cc-worker
--psk <key> Paired-device pre-shared key. Omit only for a loopback dev server.

CC_WORKER_CACHE overrides the worktree materialization cache directory (default: a cc_worker_cache directory under the system temp dir).

The worker registers with fleet.registerWorker, then heartbeats every 20s and polls for leases every 2s. It runs until SIGINT/SIGTERM; put it under systemd or launchd so it restarts and re-registers after a crash or reboot.

Open Observability in the desktop or web app, switch to the Live tab and scroll to the bottom. Workers lists every registered worker with its platform, cores, capability keys and last heartbeat; Jobs lists the jobs distributed across them.

Your worker appearing there with a recent heartbeat is the confirmation that pairing and registration worked.

From a worker’s tile you can drain it (finish current jobs, take no new leases), resume a draining worker, revoke its credential, or remove the row.

Worker and job rows live in the server’s global.db — the worker itself writes nothing to disk beyond the materialization cache.

On registration the worker auto-detects its capabilities and the scheduler matches jobs against them. You do not configure these by hand:

  • OS and arch (macos/linux/windows, arm64/x64) — exact probes, with fallbacks to uname -m / PROCESSOR_ARCHITECTURE
  • Cores and RAM — exact core count; RAM degrades to 0 when unprobeable
  • flutter — set when a Flutter SDK is reachable (bare flutter, then fvm flutter); golden-render jobs require it
  • sandboxnative-macos on macOS, native-linux on Linux. This is a placement hint, not a promise: the worker advertises the backend its platform has, but it does not wrap the jobs it runs in one. Unlike cc_server, cc_worker spawns a leased job directly
  • always-on and parallel — a dedicated cc_worker always advertises both, so it is eligible for eval batches and other throwaway parallel capacity

Detection is best-effort: anything it cannot probe degrades to a safe default rather than failing startup.

The lease protocol carries six job kinds:

Kind What it is
agentRun A single agent run (dispatch)
pipelineStep A pipeline step execution
codeIndex A repo code-index build
goldenRender A UI visual golden render (requires flutter)
benchmark A performance benchmark run
evalBatch An eval batch (prefers parallel capacity)

Execution is a real subprocess-streaming implementation, not a full embedded agent runtime. When a lease carries repoRemote, the worker does git clone --depth 1 into a remote-and-SHA-keyed cache directory, then git fetch and git checkout. For agentRun it runs the command in the lease env’s CC_JOB_COMMAND in the work directory, streaming stdout and stderr back as events; with no CC_JOB_COMMAND it echoes the prompt so the transport is still exercised end to end. The other kinds run small real commands (git rev-parse HEAD, git --version) as honest probes. Job-scoped credentials in the lease env are injected into every subprocess and never written to a log.

Events are batched and flushed every 250ms or every 32 events; each job ends with a done event and a completion report. The model is heartbeats plus short-TTL leases: if a worker loses contact its lease expires and the server reaps the job for retry. There is no durable state on the worker and no consensus or work-stealing between workers.