> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.conductorquantum.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.conductorquantum.com/_mcp/server.

# Protocol

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:

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

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

```json
{
  "machine_fingerprint": "hostname-macaddr",
  "connectivity": [[1, 0], [2, 1]]
}
```

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:

| File                    | Contents                                                                |
| ----------------------- | ----------------------------------------------------------------------- |
| `/tmp/coda.config`      | QPU identity, Redis URL, API paths, VPN settings, and private-key path. |
| `/tmp/coda-private-key` | PEM-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:

```text
qpu:<qpu_id>:jobs
```

`coda-node` creates or joins a consumer group:

```text
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:

```text
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:

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

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:

```http
POST <callback_url>
Authorization: Bearer <jwt>
Content-Type: application/json
```

```json
{
  "job_id": "uuid",
  "status": "completed",
  "counts": {"00": 512, "11": 488},
  "execution_time_ms": 42.5,
  "shots_completed": 1000
}
```

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

```json
{
  "job_id": "uuid",
  "status": "failed",
  "error": "Qubit calibration timeout after 30s"
}
```

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.