# Protocol

> The NYXDB client protocol — HTTP routes for sessions, one-shot SQL and writes, and the multiplexed WebSocket session with its frames.

A node's client listener serves HTTP routes and one WebSocket route. The route
and frame names below are the ones the gateway uses.

## HTTP routes

| Method and route | Purpose |
| --- | --- |
| `GET /auth/issuers` | Lists the credential issuers this node offers. An empty list means bearer login only. |
| `POST /auth/password` | Exchanges a user name and password for a short-lived bearer. 404 when the node has no issuer key. |
| `POST /auth/api-key` | Exchanges an API key for a short-lived bearer. 404 when the node has no issuer key. |
| `POST /auth/session` | Exchanges a bearer for a session. The body is an empty JSON object, `{}`. |
| `GET /auth/session` | Reports the session named by the `Authorization` header. |
| `DELETE /auth/session` | Closes the session. Later requests with its token answer 401. |
| `POST /auth/session/refresh` | Renews a renewable session. |
| `POST /sql` | Runs one statement and returns its whole result. |
| `POST /write/batch` | Writes a bounded batch of rows to one table. |
| `POST /bulk-load/frame` | Sends one native columnar bulk-load frame. |
| `POST /insert/logical-block` | Sends one logical insert block (`Content-Type: application/vnd.nyxdb.logical-insert`). |

Every other target answers 404. JSON routes require
`Content-Type: application/json`. A CORS preflight (`OPTIONS`) must carry an
`Origin` header.

### Open a session

1. Obtain a bearer: from `POST /auth/password` or `POST /auth/api-key`, or an
   Ed25519 bearer minted for the principal.
2. `POST /auth/session` with `Authorization: Bearer <bearer>` and the body `{}`.
3. The answer carries `session_token`, `principal_id`, `tenant_id`,
   `expires_at` and `renewable`; a renewable session also carries a
   `refresh_grant`.
4. Send `Authorization: Bearer <session_token>` on every later request.

### One-shot SQL

`POST /sql` takes a JSON document:

| Field | Meaning |
| --- | --- |
| `sql` | One statement, at most 64 KiB. |
| `maximum_rows` | Required. The most rows the answer may carry. |
| `parameters` | Optional. Values for `?` placeholders, each `{"kind": …, "text": …}`, for example `{"kind":"string","text":"…"}`. |

A read answers with a JSON document whose `schema` is `nyxdb.query-result/2`:
the column definitions, the rows with each value's canonical bytes, and a
receipt with counters such as `delivered_rows`. DDL and writes answer with a
command receipt. Errors answer with an HTTP status and a message, for example
400 for a statement that does not bind and 403 for a missing permission.

Session statements — `SET`, `RESET` and the federation statements — run on a
WebSocket session, where their effect lasts for the session.

## WebSocket session

| Property | Value |
| --- | --- |
| Route | `GET /client`, an HTTP/1.1 upgrade |
| Subprotocol | `nyxdb.public.v1` |
| First frame | `OpenSession` with the session token from `POST /auth/session` |
| Transport security | `wss://` for any host other than loopback |
| Browsers | The `Origin` must equal a configured origin (`gateway.http.allowed_origin` or `gateway.http.allowed_origins`) |

The session token travels only in the first frame. It never goes in the URL, a
cookie or the subprotocol. Frames are binary with a fixed header in network
byte order.

### Client frames

| Frame | Purpose |
| --- | --- |
| `OpenSession`, `OpenSessionExtended` | Bind the connection to a session; the extended form also negotiates protocol and capability ranges. |
| `ExecuteSql` | Run one statement, with `maximum_rows`, `maximum_bytes` and `initial_demand`. |
| `ExecuteSqlPrepared` | The same with bound `?` parameters. |
| `ExecuteQuery` | The same with parameters plus opt-in features: progress, profile and evidence frames. |
| `Demand` | Grant more result credit to a request. |
| `ContinuousDemand` | Acknowledge a delivered prefix of a continuous result and grant more credit. |
| `Cancel` | Cancel the request named by the frame's request id. |
| `ResumeStream`, `ResumeContinuous` | Re-open a system stream or a data-relation continuous subscription after a delivery cursor. |
| `Ping` | Liveness check, answered by `Pong`. |
| `Close` | End the connection. |

The protocol also carries administrative commands (used for roles and grants,
see [Governance](/docs/operations/governance)), application sessions for
federation, and native bulk and logical insert frames.

### Server frames

| Frame | Purpose |
| --- | --- |
| `SessionOpened`, `SessionOpenedExtended` | The session is bound. |
| `ResultBatch` and exact result batches | One page of a finite result. |
| `Completed` | The request finished; carries the receipt and elapsed time. |
| `Failed` | The request failed or was cancelled; carries the status. |
| `QueryProgress` | Stage (`queued`, `executing`, `delivering`, `finalizing`) and delivered counters. Never consumes demand. |
| `ContinuousOpened` | A continuous query is open; carries its delivery cursor. |
| `ContinuousResult` | One snapshot chunk, delta or replacement snapshot of a continuous query. |
| `StreamOpened`, `StreamChange` | Snapshot and changes of a system-relation stream. |
| `Pong` | Answer to `Ping`. |

Every frame carries a request id. Many requests run at once on one connection
and are told apart by it; see [Query lifecycle](/docs/drivers/query-lifecycle).

## What travels where

| Need | Transport |
| --- | --- |
| Log in, open or close a session | HTTP |
| One-shot SQL over tables and views | HTTP `POST /sql` or WebSocket |
| `STREAM SELECT` | WebSocket |
| `SET`, `RESET`, federation statements, system relations | WebSocket |
| Bulk rows | HTTP `/write/batch`, `/bulk-load/frame`, `/insert/logical-block`, or the WebSocket bulk frames |

The [TypeScript client](/docs/drivers) implements both transports.

---

Source: https://nyxdb.io/docs/drivers/protocol/
