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.
Prerequisites
Section titled “Prerequisites”- 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). giton thePATH— the worker materializes repos and runsgitprobes.
Step 1: Build the binary
Section titled “Step 1: Build the binary”cd apps/cc_workerfvm dart build cliThis produces a self-contained bundle at ./build/cli/<os_arch>/bundle/bin/cc_worker.
Step 2: Mint the worker’s credential
Section titled “Step 2: Mint the worker’s credential”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:
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.)
Step 3: Start the worker
Section titled “Step 3: Start the worker”Against a paired server:
./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:
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.
Step 4: Verify it in the fleet panel
Section titled “Step 4: Verify it in the fleet panel”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.
What the worker advertises
Section titled “What the worker advertises”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 touname -m/PROCESSOR_ARCHITECTURE - Cores and RAM — exact core count; RAM degrades to
0when unprobeable flutter— set when a Flutter SDK is reachable (bareflutter, thenfvm flutter); golden-render jobs require itsandbox—native-macoson macOS,native-linuxon 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. Unlikecc_server,cc_workerspawns a leased job directlyalways-onandparallel— a dedicatedcc_workeralways 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.
What the worker would execute
Section titled “What the worker would execute”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.