Repository Public
1# API23The browser API is private: it exists for the bundled web application and for the single administrator.4The reference is generated by the running server:56```7GET /api/v1/openapi.json (authenticated; OpenAPI 3.1)8```910That document is assembled from the live route table and the zod schemas attached to each route11(`apps/server/src/modules/openapi.ts`), so it cannot drift from what is registered. Each operation carries12`x-auth` (`public` | `preauth` | `restricted` | `full`) and `x-websocket`. The human-readable endpoint13table is in [CONTRACT.md](CONTRACT.md) ("Browser API"); request and response types are in14`packages/shared/src/dto.ts` and `wire.ts`.1516This page only states the conventions.1718## Conventions1920| Topic | Rule |21|---|---|22| Base path | `/api/v1`; JSON in and out; timestamps are ISO-8601 strings |23| Authentication | session cookie `__Host-cr_sid` (HttpOnly, Secure, SameSite=Strict). See [authentication.md](authentication.md) |24| Mutations (`POST`, `PUT`, `PATCH`, `DELETE`) | header `X-CSRF-Token` (from `GET /auth/status`) **and** `Origin` equal to the public origin |25| WebSocket upgrades | cookie + completed second factor + matching `Origin`; a refused upgrade is a plain HTTP error (401, 403, 404, 410), never a socket |26| Errors | `{"error": {"code": string, "message": string, "details"?: any}}` with a meaningful HTTP status |27| Validation | every body, query and path parameter is zod-validated; failure = 400 `invalid_request` with `details: [{path, message}]` |28| Caching | every `/api/` response is `Cache-Control: no-store` |29| Paths | file paths are relative to the project root (`""` = root); absolute host paths are never exposed, `..` is refused |30| Limits | JSON body 6 MiB; file read 2 MiB (text only); file write 5 MiB; upload 200 MiB (raw body); WebSocket frame 1 MiB |31| Rate limits | per minute: credential endpoints 20 per IP; anonymous `GET /auth/status` 240 per IP; `GET /health` 600 per IP; everything else 600 per session. Excess = 429 `rate_limited` |3233Common error codes: `unauthenticated` (401), `second_factor_required`, `bad_origin`, `bad_csrf` (403),34`not_found` (404), `conflict`, `exists`, `invalid_state` (409), `payload_too_large` (413), `binary` (415),35`locked`, `rate_limited`, `too_many_sessions` (429), `not_implemented` (501), `runtime_error` (502),36`runtime_unavailable` (503).3738## Areas3940| Area | Paths | Notes |41|---|---|---|42| Health | `GET /health` | the only public route; returns `{ok, version}` |43| Auth | `/auth/*` | setup, login, TOTP, passkeys, devices, password, logout |44| Projects | `/projects`, `/projects/:id`, `/workspace/roots` | `DELETE` unregisters only; files are never deleted |45| Sessions | `/sessions`, `/sessions/:id`, `…/terminate`, `…/reconnect`, `…/snapshot`, `/projects/:id/conversations` | Claude arguments are derived from `resumeConversationId` / `continueLast` only |46| Files | `/projects/:id/files`, `…/file`, `…/fs`, `…/upload`, `…/download`, `…/search`, `…/git/status`, `…/git/diff` | write uses `baseSha256` for optimistic concurrency (409 `conflict` with the on-disk `sha256`) |47| Tasks | `/tasks`, `/tasks/:id`, `…/cancel`, `…/retry`, `…/log`, `…/artifacts/:artifactId` | `host` = `local` or an allow-listed alias; retry only when `idempotent` |48| Cluster | `/cluster/nodes`, `/cluster/nodes/:id`, `/cluster/metrics`, `/cluster/models`, `/cluster/models/:id/test` | read-only telemetry |49| Skills / MCP | `/skills`, `/skills/op`, `/skills/registry/search`, `/mcp`, `/mcp/:name`, `/mcp/:name/test` | MCP env and header values are write-only |50| System | `/system/status`, `/system/audit` | component health with per-check timestamps; audit paginated with `before` |51| Misc | `/notifications`, `/notifications/read`, `/settings`, `/openapi.json` | |5253## WebSockets5455| Path | Frames |56|---|---|57| `/sessions/:id/stream?cols=&rows=` | binary = raw PTY bytes both ways; text = JSON control messages (`resize`, `ping` from the client; `ready`, `pong`, `exit`, `error` from the server) |58| `/tasks/:id/log/stream` | binary log bytes, server to client only; closes when the task has ended and the log is drained |59| `/events` | text JSON `ServerEvent`: `hello`, `session.updated`, `session.removed`, `task.updated`, `notification`, `auth.expired` |6061Application close codes: 4401 unauthorized, 4403 forbidden, 4404 not found, 4410 session ended,624429 backpressure, 4503 runtime unavailable.6364## Runtime IPC (internal)6566The runtime's HTTP/WebSocket interface on the unix socket is not a public API. It is specified in67[CONTRACT.md](CONTRACT.md) ("Runtime IPC") and typed in `packages/shared/src/wire.ts`. Only68`apps/server` holds its token.6970## Limitations / not verified7172- There is no API token or service-account mechanism: the API can only be used with a browser session73 cookie. It is not designed for third-party clients.74- The OpenAPI document describes request and response schemas only for routes that declare them;75 streaming endpoints (upload, download, logs) and WebSockets are listed without body schemas.76- No versioning policy beyond the `/api/v1` prefix; the shared package is at version 0.1.0.77- `apps/web/dev/mock-server.mjs` is a test double for the Playwright suite; it is not a reference78 implementation of this API.79