# DB-Sync Client-Server Protocol ## Transport - WebSocket `ws(s)` to `/sync/:graph-id`. - Client builds URL from config and appends `?token=...` when available. - Encoding: JSON objects; `tx` payloads are Transit strings. - Note: keep this document in sync with the current implementation. ## Client -> Server - `{"type":"hello","client":""}` - Initial handshake from client. - `{"type":"pull","since":}` - Request txs after `since` (defaults to 0). - `{"type":"tx/batch","t_before":,"txs":["", ...]}` - Upload a batch of txs based on `t_before` (required). - `{"type":"ping"}` - Optional keepalive; server replies `pong`. ## Server -> Client - `{"type":"hello","t":}` - Server hello with current t. - `{"type":"pull/ok","t":,"txs":[{"t":,"tx":""}...]}` - Pull response with txs. - `{"type":"tx/batch/ok","t":}` - Batch accepted; server advanced to t. - `{"type":"changed","t":}` - Broadcast that server state advanced; client should pull. - `{"type":"tx/reject","reason":"stale","t":}` - Client tx is based on stale t. - `{"type":"tx/reject","reason":"cycle","data":" :server_values ...}>"}` - Cycle detected with server values. - `{"type":"tx/reject","reason":"empty tx data"|"invalid tx"|"invalid t_before"}` - Invalid batch. - `{"type":"pong"}` - Keepalive response. - `{"type":"error","message":"..."}` - Invalid/unknown message. Current messages: `"unknown type"`, `"invalid request"`, `"server error"`, `"invalid since"`. ## HTTP API - Auth: Bearer token via `Authorization: Bearer ` or `?token=...`. - JSON body/response unless noted. - Auth required for `/graphs`, `/sync/:graph-id/*`, and `/assets/*`. Expect `401` (unauthorized) or `403` (forbidden) on access failure. ### Worker Health - `GET /health` - Worker health check. Response: `{"ok":true}`. ### Graphs (index DO) - `GET /graphs` - List graphs the user owns. Response: `{"graphs":[{graph_id, graph_name, schema_version?, created_at, updated_at}...]}`. - `POST /graphs` - Create graph. Body: `{"graph_name":"...","schema_version":""}` (schema_version optional). Response: `{"graph_id":"..."}`. - `GET /graphs/:graph-id/access` - Access check. Response: `{"ok":true}`, `401` (unauthorized), `403` (forbidden), or `404` (not found). - `DELETE /graphs/:graph-id` - Delete graph and reset data. Response: `{"graph_id":"...","deleted":true}` or `400` (missing graph id). ### Sync (per-graph DO, via `/sync/:graph-id/...`) - `GET /sync/:graph-id/health` - Health check. Response: `{"ok":true}`. - `GET /sync/:graph-id/pull?since=` - Same as WS pull. Response: `{"type":"pull/ok","t":,"txs":[{"t":,"tx":""}...]}`. - Error response (400): `{"error":"invalid since"}`. - `POST /sync/:graph-id/tx/batch` - Same as WS tx/batch. Body: `{"t_before":,"txs":["", ...]}`. - Response: `{"type":"tx/batch/ok","t":}` or `{"type":"tx/reject","reason":...}`. - Error response (400): `{"error":"missing body"|"invalid tx"}`. - `GET /sync/:graph-id/snapshot/rows?after=&limit=` - Pull sqlite kvs rows. Response: `{"rows":[{"addr":,"content":"","addresses":}...],"last_addr":,"done":true|false}`. - `POST /sync/:graph-id/snapshot/import` - Import sqlite kvs rows. Body: `{"reset":true|false,"rows":[[addr,content,addresses]...]}`. - Response: `{"ok":true,"count":}`. - Error response (400): `{"error":"missing body"|"invalid body"}`. - `DELETE /sync/:graph-id/admin/reset` - Drop/recreate per-graph tables. Response: `{"ok":true}`. ### Assets - `GET /assets/:graph-id/:asset-uuid.:ext` - Download asset (binary response, `content-type` set, `x-asset-type` header included). - `PUT /assets/:graph-id/:asset-uuid.:ext` - Upload asset (binary body). Size limit ~100MB. Response: `{"ok":true}`. - `DELETE /assets/:graph-id/:asset-uuid.:ext` - Delete asset. Response: `{"ok":true}`. - Asset error responses: `{"error":"invalid asset path"}` (400), `{"error":"not found"}` (404), `{"error":"asset too large"}` (413), `{"error":"method not allowed"}` (405), `{"error":"missing assets bucket"}` (500).