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 --token or CODA_NODE_TOKEN.
  • Reconnect: omit the token and use persisted JWT credentials from a previous successful connect.

Both modes call the Coda connect endpoint:

1POST /api/internal/qpu/connect
2Authorization: Bearer <node-token-or-jwt>
3Content-Type: application/json

The request includes a stable machine fingerprint and, when available, the node’s live connectivity:

1{
2 "machine_fingerprint": "hostname-macaddr",
3 "connectivity": [[1, 0], [2, 1]]
4}

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:

  1. Updating runtime settings such as qpu_id, native gate set, qubit count, Redis URL, and API paths.
  2. Writing the private key and runtime config to disk.
  3. Starting OpenVPN when the bundle requires VPN mode and includes a client profile.
  4. 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:

FileContents
/tmp/coda.configQPU identity, Redis URL, API paths, VPN settings, and private-key path.
/tmp/coda-private-keyPEM-encoded RSA private key for signing node JWTs.

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:

qpu:<qpu_id>:jobs

coda-node creates or joins a consumer group:

qpu:<qpu_id>:workers

Each job includes a job_id, callback_url, requested shots, and serialized NativeGateIR. The consumer:

  1. Skips jobs that were already completed or cancelled.
  2. Marks the job as executing in Redis.
  3. Deserializes ir_json into NativeGateIR.
  4. Calls executor.run(ir, shots).
  5. Sends a completion or failure webhook.
  6. 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:

qpu:job:cancelled:<job_id>

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:

1{
2 "current_job": "job-id-or-null",
3 "last_job_at": "2026-04-27T00:00:00Z",
4 "redis_healthy": true,
5 "connectivity": [[1, 0], [2, 1]]
6}

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:

1POST <callback_url>
2Authorization: Bearer <jwt>
3Content-Type: application/json
1{
2 "job_id": "uuid",
3 "status": "completed",
4 "counts": {"00": 512, "11": 488},
5 "execution_time_ms": 42.5,
6 "shots_completed": 1000
7}

Failures use the same webhook path with status: "failed" and an error message:

1{
2 "job_id": "uuid",
3 "status": "failed",
4 "error": "Qubit calibration timeout after 30s"
5}

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.