Protocol
How coda-node connects, receives jobs, and reports results.
The node protocol is the contract between Coda and a QPU operator running coda-node. Your executor only implements hardware execution; the node runtime handles provisioning, credentials, health, job delivery, and result webhooks.
Startup and connect
On startup, coda-node loads CODA_ environment variables, then persisted runtime state, then defaults.
The node connects to Coda in one of two modes:
- First run: pass a one-time node token with
--tokenorCODA_NODE_TOKEN. - Reconnect: omit the token and use persisted JWT credentials from a previous successful connect.
Both modes call the Coda connect endpoint:
The request includes a stable machine fingerprint and, when available, the node’s live connectivity:
For directed gate sets such as CNOT, connectivity edges are ordered as [control, target]. For symmetric gate sets such as CZ, edge order is ignored by the cloud compiler.
Provisioning bundle
Coda responds with a runtime bundle containing QPU identity, JWT key metadata, Redis connection details, API paths, and VPN settings.
The node applies the bundle by:
- Updating runtime settings such as
qpu_id, native gate set, qubit count, Redis URL, and API paths. - Writing the private key and runtime config to disk.
- Starting OpenVPN when the bundle requires VPN mode and includes a client profile.
- Initializing the FastAPI service, Redis consumer, heartbeat loop, and webhook client.
In HTTPS mode, the VPN block marks VPN as not required, so the node skips OpenVPN setup and uses TLS directly for API, Redis, and webhook traffic.
Persisted credentials
After a successful connect, the node writes:
Both files use 0600 permissions on POSIX systems. On future starts, if no node token is provided, coda-node loads these credentials and reconnects with a JWT.
Use uv run coda-node reset to remove persisted state and force a fresh node-token provisioning flow.
Job delivery
Coda places compiled jobs on a Redis Stream for the QPU:
coda-node creates or joins a consumer group:
Each job includes a job_id, callback_url, requested shots, and serialized NativeGateIR. The consumer:
- Skips jobs that were already completed or cancelled.
- Marks the job as
executingin Redis. - Deserializes
ir_jsonintoNativeGateIR. - Calls
executor.run(ir, shots). - Sends a completion or failure webhook.
- Acknowledges the Redis stream message.
If the executor exposes batch_run(jobs), the consumer reads up to 10 jobs at a time and calls the batch hook automatically.
Cancellation
Coda can cancel a queued or running job by writing:
Before dispatching a job, the node checks this key and skips cancelled work. While a job is running, the node polls the key. If it appears, the node calls the executor’s optional cancel_current_job() hook, cancels the in-process task, marks the Redis status as cancelled, and suppresses terminal webhooks.
Heartbeats
The node sends authenticated heartbeats to Coda, usually every 30 seconds. The heartbeat body mirrors readiness state:
Heartbeats keep the QPU visible as online and update topology for compiler routing. If the node misses several heartbeats, Coda marks the QPU offline.
Result webhooks
For successful jobs, the node signs a JWT with its private key and posts the result to the job callback URL:
Failures use the same webhook path with status: "failed" and an error message:
Webhook delivery retries transient 5xx and transport failures with exponential backoff. 4xx responses fail immediately.
Readiness and shutdown
The local FastAPI service exposes:
GET /health: liveness only.GET /ready: readiness for VPN, Redis, and the current job state.
On shutdown, coda-node stops accepting work, drains the in-flight job up to CODA_SHUTDOWN_DRAIN_TIMEOUT_SEC, closes background clients, and stops the managed VPN daemon when applicable.