{ "openapi": "3.1.0", "info": { "title": "WAI room service", "version": "0.1.0", "license": { "name": "Apache-2.0", "identifier": "Apache-2.0" }, "description": "The HTTP API of the room service (`joule-moq-room`), which keeps WAI live rooms (`wai/extensions/live-rooms.md`) over a relay: it issues grants, authors each room's lifecycle on its clock's events track, and serves the signed room. A configuration turns on the modes a room keeps: end-to-end encryption (`[room] e2ee`), admission by age (`[age]`), co-performance (`[co]`), a watch room (`[room] watch_source`), a remote desktop room (`[remote]`), a conversation (`[messaging]`) or calls (`[call]`). Each mode's paths answer 404 in a service that does not keep it.\n\nEvery answer but a watch room's source is JSON with `Cache-Control: no-store`. Each operation's `operationId` is its name in the WAI toolchain's catalogue, `noun.verb` (`room.grant`), the name every other door derives its own from: the MCP tool `room_grant`, the command `wai room grant`. A path answers 405 to a method it does not take (`/msg/*` and `/call/*` paths answer 404). The statuses listed for each operation are those its handler can return; `tests/openapi_contract.rs` holds this description to the service, route by route and status by status.\n\nRequests that a principal authorises carry its grant as `Authorization: Bearer ` and a proof of possession of the grant's key in `WAI-Room-Proof`, made over a challenge the service issued in `WAI-Room-Challenge` (live-rooms §10.5.3); a request that proves only a key carries the proof alone, naming no grant. The service listens on loopback behind a front that serves it under `public_prefix`; a proof names the path the client addressed." }, "servers": [ { "url": "http://127.0.0.1:9682", "description": "the example configuration's address (`room.example.toml`)" }, { "url": "http://127.0.0.1:8080/room-api", "description": "the service behind `wai dev`'s front, on its default port" } ], "tags": [ { "name": "room", "description": "every room" }, { "name": "age", "description": "admission by age (age-assurance §5.6)" }, { "name": "e2ee", "description": "end-to-end encrypted rooms (confidentiality)" }, { "name": "co-performance", "description": "co-performance" }, { "name": "watch", "description": "watch rooms (co-watch)" }, { "name": "remote", "description": "remote desktop rooms (remote-desktop)" }, { "name": "messaging", "description": "a conversation (live-messaging)" }, { "name": "calls", "description": "calls (live-messaging §10)" } ], "paths": { "/": { "get": { "operationId": "service.show", "tags": [ "room" ], "summary": "The service's health", "description": "As `GET /health`.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "relay": { "type": "boolean", "description": "the clock's session on the relay is open" }, "room": { "$ref": "#/components/schemas/Hex32" } }, "required": [ "ok", "relay", "room" ] } } } } } } }, "/health": { "get": { "operationId": "service.check", "tags": [ "room" ], "summary": "The service's health", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "relay": { "type": "boolean", "description": "the clock's session on the relay is open" }, "room": { "$ref": "#/components/schemas/Hex32" } }, "required": [ "ok", "relay", "room" ] } } } } } } }, "/room": { "get": { "operationId": "room.show", "tags": [ "room" ], "summary": "The current room", "description": "The room a page verifies before it enters (live-rooms §9): its id, `t0_us`, the signed descriptor and composition descriptor, the publishers' grants, how many publish and may, and the record of each mode the room keeps (end-to-end encryption, remote desktop, admission by age, co-performance, watch, shared world), with the service's clock in `now_us`.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Room" } } } } } } }, "/grant": { "post": { "operationId": "room.grant", "tags": [ "room" ], "summary": "A grant for a key", "description": "A short-lived WRG1 grant for the key, which a relay honours only beside a proof of possession made with that key (live-rooms §10.5). The body is at most 1 KiB. In a room that admits by age the request carries `Authorization: PrivateToken token=\"…\"` (RFC 9577): without one the answer is 401 with a `WWW-Authenticate: PrivateToken` challenge for each key of the room's policy. A call room's grants come with its call (`/call/place`, `/call/answer`): there it is 403.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "principal": { "$ref": "#/components/schemas/Hex32" }, "role": { "type": "string", "enum": [ "publisher", "viewer", "host", "controller" ], "description": "publisher or viewer; in a remote desktop room host, controller or viewer" }, "ai": { "$ref": "#/components/schemas/Hex32", "description": "a publisher's, in a room with co-performance that takes AI participants: the digest of the model that acts for it (live-rooms §4.4)" }, "invitation": { "type": "string", "description": "a remote desktop room's host without a configured key: an invitation the room's authority signed" } }, "required": [ "principal", "role" ] } } } }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GrantAnswer" } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "a room that admits by age, and no token: `{\"error\": \"token_required\", \"age\"}` with its challenges", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "a token refused (age-assurance §11's reason), a remote desktop room's host or controller refused (`not_the_host`, `invitation_required`, `invitation_used`, `invitation_expired`, `invitation_bad_signature`, `control_not_allowed`), or a call room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "`full` (the room has its publishers), `busy` (it has taken its grants), or `hosted` (a remote room's host holds its place)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "past the client's rate: retry after retry_after_s", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "`relay`: the clock's session on the relay is not open; or, admitting by age, the window's nonces are at their bound", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/age/receipt": { "get": { "operationId": "admission.show", "tags": [ "age" ], "summary": "The room's admission receipt so far", "description": "In a room that admits by age (`[age]`); 404 in any other.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "receipt": { "type": "object", "description": "the wai-age-admissions receipt (age-assurance §7), signed by the room's clock, every count under 50 suppressed" }, "policy": { "type": "string" } } } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "past the client's rate: retry after retry_after_s", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/mls": { "get": { "operationId": "mls.tail", "tags": [ "e2ee" ], "summary": "The room's log of MLS messages", "description": "An end-to-end encrypted room's log (confidentiality §5.1, §15.2) from entry `from`: the clock's lifecycle records (`event`) and every GroupInfo, commit and Welcome carried (`mls`), in the order the clock took them; 404 in a room that is not end-to-end encrypted.", "parameters": [ { "name": "room", "in": "query", "required": true, "schema": { "$ref": "#/components/schemas/Hex32" }, "description": "the room's id" }, { "name": "from", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0 }, "description": "the first entry wanted (default 0)" }, { "name": "wait_ms", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 8000 }, "description": "how long to hold the request while the log has nothing past `from` (default and most 8000)" } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "entries": { "type": "array", "items": { "type": "object", "properties": { "seq": { "type": "integer" }, "event": { "$ref": "#/components/schemas/Hex" }, "mls": { "$ref": "#/components/schemas/Hex" } } } }, "next": { "type": "integer" }, "end": { "type": [ "integer", "boolean" ] } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/mls/key-package": { "post": { "operationId": "mls.import", "tags": [ "e2ee" ], "summary": "A KeyPackage for a granted principal's Add", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "key_package": { "$ref": "#/components/schemas/Hex" } }, "required": [ "room", "key_package" ] } } } }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "principal": { "$ref": "#/components/schemas/Hex32" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not granted`: the KeyPackage's principal holds no grant of the room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/mls/post": { "post": { "operationId": "mls.update", "tags": [ "e2ee" ], "summary": "A GroupInfo, or a commit and its Welcome", "description": "Checked by the room's clock (confidentiality §5.5), then carried in the log. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "frames": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" }, "minItems": 1 } }, "required": [ "room", "frames" ] } } } }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "the clock refuses it (its reason); it is not carried", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/co/objects": { "get": { "operationId": "performance.list", "tags": [ "co-performance" ], "summary": "The clock's log of objects", "description": "Co-performance §9.1, from entry `from`, at most 5000 an answer; 404 in a room without co-performance.", "parameters": [ { "name": "from", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0 }, "description": "the first entry wanted (default 0)" } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "objects": { "type": "array", "items": { "type": "object" } }, "next": { "type": "integer" }, "end": { "type": "boolean" }, "unlogged": { "type": "integer" } } } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/co/state": { "get": { "operationId": "performance.show", "tags": [ "co-performance" ], "summary": "The room's co-performance as the clock holds it", "description": "Its windows, the marks it signed and those it owes, its log's length and its own records, at room-clock `now`.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/co/schedule": { "post": { "operationId": "schedule.create", "tags": [ "co-performance" ], "summary": "A turn schedule the clock signs", "description": "Co-performance §6.1, for a grant with the direct power; `from_us` at least `lead_ms` ahead. Authorised by a grant of the room (`Authorization: Bearer` and the grant's record in base64url) and its proof of possession (`WAI-Room-Proof`) over a challenge the service issued in `WAI-Room-Challenge`, on a 401 and on every answer it accepts (live-rooms §10.5.3); the proof's context is the method and the path the client addressed. The body is at most 4 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "mode": { "type": "string", "enum": [ "mark", "refuse" ] }, "from_us": { "type": "integer" }, "beat_us": { "type": "integer" }, "beats_per_bar": { "type": "integer" }, "segments": { "type": "array", "items": { "type": "object", "properties": { "start_us": { "type": "integer" }, "length": { "type": "integer" }, "turns": { "type": "integer" }, "pairs": { "type": "boolean" }, "holders": { "type": "array", "items": { "$ref": "#/components/schemas/Hex32" } } } } } }, "required": [ "mode", "segments" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "event": { "$ref": "#/components/schemas/Hex32" }, "t": { "type": "integer" }, "statement": { "$ref": "#/components/schemas/Hex" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "the grant's powers do not allow it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the clock could not sign it now", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/co/handover": { "post": { "operationId": "handover.create", "tags": [ "co-performance" ], "summary": "A handover the clock signs", "description": "Co-performance §6.3, for the holder of the turn at `at_us` (now and `lead_ms` when left out) or a grant with the direct power. Authorised by a grant of the room (`Authorization: Bearer` and the grant's record in base64url) and its proof of possession (`WAI-Room-Proof`) over a challenge the service issued in `WAI-Room-Challenge`, on a 401 and on every answer it accepts (live-rooms §10.5.3); the proof's context is the method and the path the client addressed. The body is at most 4 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "to": { "$ref": "#/components/schemas/Hex32" }, "at_us": { "type": "integer" } }, "required": [ "to" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "event": { "$ref": "#/components/schemas/Hex32" }, "t": { "type": "integer" }, "statement": { "$ref": "#/components/schemas/Hex" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "the grant's powers do not allow it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the clock could not sign it now", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/co/window": { "post": { "operationId": "window.create", "tags": [ "co-performance" ], "summary": "A selection window the clock signs", "description": "Co-performance §7.2, for a grant with the direct power; a draw's seed is drawn by the clock and kept until its selection. Authorised by a grant of the room (`Authorization: Bearer` and the grant's record in base64url) and its proof of possession (`WAI-Room-Proof`) over a challenge the service issued in `WAI-Room-Challenge`, on a 401 and on every answer it accepts (live-rooms §10.5.3); the proof's context is the method and the path the client addressed. The body is at most 4 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "rule": { "type": "string", "enum": [ "plurality", "reactions", "draw" ] }, "types": { "type": "array", "items": { "type": "string", "enum": [ "prompt", "sketch", "vote", "movement" ] } }, "options": { "type": "integer" }, "k": { "type": "integer" }, "close_us": { "type": "integer" }, "seconds": { "type": "integer" }, "question": { "type": "string" } }, "required": [ "rule" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "event": { "$ref": "#/components/schemas/Hex32" }, "t": { "type": "integer" }, "statement": { "$ref": "#/components/schemas/Hex" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "the grant's powers do not allow it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the clock could not sign it now", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/co/fidelity": { "post": { "operationId": "fidelity.create", "tags": [ "co-performance" ], "summary": "A fidelity statement now", "description": "Co-performance §5.6, for a grant with the direct power; `{\"ok\": false, \"reason\"}` while no intent is covered yet. Authorised by a grant of the room (`Authorization: Bearer` and the grant's record in base64url) and its proof of possession (`WAI-Room-Proof`) over a challenge the service issued in `WAI-Room-Challenge`, on a 401 and on every answer it accepts (live-rooms §10.5.3); the proof's context is the method and the path the client addressed. The body is at most 4 KiB.", "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "oneOf": [ { "type": "object", "properties": { "ok": { "type": "boolean" }, "event": { "$ref": "#/components/schemas/Hex32" }, "t": { "type": "integer" }, "statement": { "$ref": "#/components/schemas/Hex" } } }, { "type": "object", "properties": { "ok": { "const": false }, "reason": { "type": "string" } } } ] } } } }, "400": { "description": "a body that is not JSON", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "the grant's powers do not allow it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "the service could not read its own record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the clock could not sign it now", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/watch/time": { "get": { "operationId": "time.show", "tags": [ "watch" ], "summary": "The service's clock", "description": "For a page's estimate of the room clock (co-watch).", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "now_us": { "type": "integer" }, "t0_us": { "type": "integer" } } } } } } } } }, "/watch/source": { "get": { "operationId": "source.fetch", "tags": [ "watch" ], "summary": "The watch room's source, whole", "description": "The source's bytes, of its media type; a page checks them by their SHA-256, the content hash its policy names (co-watch §3.5). 404 in a room that watches nothing.", "responses": { "200": { "description": "the source's bytes", "content": { "*/*": { "schema": { "type": "string", "format": "binary" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/watch/challenge": { "get": { "operationId": "watch.setup", "tags": [ "watch" ], "summary": "A challenge for a join's proof", "description": "Single use, good for a minute (live-rooms §10.5.3).", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "challenge": { "$ref": "#/components/schemas/Hex32" } } } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/watch": { "get": { "operationId": "watch.tail", "tags": [ "watch" ], "summary": "The watch room's log", "parameters": [ { "name": "room", "in": "query", "required": true, "schema": { "$ref": "#/components/schemas/Hex32" }, "description": "the room's id" }, { "name": "from", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0 }, "description": "the first entry wanted (default 0)" }, { "name": "wait_ms", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 8000 }, "description": "how long to hold the request while the log has nothing past `from` (default and most 8000)" } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "entries": { "type": "array", "items": { "type": "object", "properties": { "seq": { "type": "integer" }, "event": { "$ref": "#/components/schemas/Hex" }, "mls": { "$ref": "#/components/schemas/Hex" } } } }, "next": { "type": "integer" }, "end": { "type": [ "integer", "boolean" ] } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/watch/room": { "get": { "operationId": "watch.show", "tags": [ "watch" ], "summary": "The room as a co-watch verifier is given it", "description": "Co-watch §11.1, with the latest skew statements read.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/watch/join": { "post": { "operationId": "watch.join", "tags": [ "watch" ], "summary": "A viewer enters by a proof of its key", "description": "The proof over a challenge from `GET /watch/challenge` for `POST ///watch/join`; the clock signs the viewer's `join`. The body is at most 4 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "grant": { "$ref": "#/components/schemas/Hex" }, "proof": { "$ref": "#/components/schemas/Hex" } }, "required": [ "room", "grant", "proof" ] } } } }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "t": { "type": "integer" }, "again": { "type": "boolean" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "a proof that fails (its reason)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the clock could not sign the join now", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/watch/post": { "post": { "operationId": "watch.update", "tags": [ "watch" ], "summary": "A viewer's events, checked and carried in order", "description": "Each event checked as co-watch §10 has it. The body is at most 16 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "events": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" }, "minItems": 1, "maxItems": 8 } }, "required": [ "room", "events" ] } } } }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "carried": { "type": "integer" }, "log": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "the first event refused (its reason, and `carried`: how many before it were carried)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/remote/control": { "post": { "operationId": "control.grant", "tags": [ "remote" ], "summary": "The host hands control over", "description": "A grant exchange (remote-desktop §7.1), the host's grant and proof presented as a co-performance request presents them; it takes effect at the first tick at least `lead_ms` ahead.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "to": { "$ref": "#/components/schemas/Hex32" }, "control": { "type": "integer", "minimum": 0, "maximum": 15, "description": "classes: 1 pointer, 2 keys, 4 touch, 8 clipboard" } }, "required": [ "to", "control" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "grant": { "$ref": "#/components/schemas/Hex" }, "digest": { "$ref": "#/components/schemas/Hex32" }, "effective": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "a grant that is not the room's host's", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "the exchange is refused (its reason, `not_present` among them)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the exchange could not be signed at its tick", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/remote/room": { "get": { "operationId": "remote.show", "tags": [ "remote" ], "summary": "The session as the clock holds it", "description": "What a verifier is given (remote-desktop §11.1). To a grant this room issued and its proof, presented as a co-performance request presents them; bounded per client.", "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "descriptor": { "$ref": "#/components/schemas/Hex" }, "grants": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" } }, "key_log": { "type": "null" }, "chains": { "type": "array", "items": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" } } } } } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "a grant of another room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "past the client's rate: retry after retry_after_s", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/remote/state": { "get": { "operationId": "remote.verify", "tags": [ "remote" ], "summary": "That session verified", "description": "Remote-desktop §11.3: the outcome, the floor's changes and the floor statements refused, each input's fate, the latencies and each host's totals, with the clock's own floor and the principals it cut. To a grant this room issued and its proof, presented as a co-performance request presents them; bounded per client.", "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "a grant of another room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "a room since replaced", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "past the client's rate: retry after retry_after_s", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg": { "get": { "operationId": "conversation.show", "tags": [ "messaging" ], "summary": "The conversation", "description": "Its descriptor, records and members (live-messaging); 404 when the service keeps no conversation.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/challenge": { "get": { "operationId": "conversation.setup", "tags": [ "messaging" ], "summary": "A challenge for a proof", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "challenge": { "$ref": "#/components/schemas/Hex32" } } } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/join": { "post": { "operationId": "conversation.join", "tags": [ "messaging" ], "summary": "A key that proves itself is admitted", "description": "A proof of possession naming no grant (live-rooms §10.5.3); the conversation's first member owns it. The clock signs its `join` (live-messaging §3.4).", "security": [ { "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "grant": { "$ref": "#/components/schemas/Hex" }, "digest": { "$ref": "#/components/schemas/Hex32" }, "exp": { "type": "integer" }, "role": { "type": "string", "enum": [ "owner", "member" ] }, "conversation": { "type": "object" } } } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_listed` or `removed`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "`full`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "429": { "description": "past the client's rate: retry after retry_after_s", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "the service could not read its own record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/log": { "get": { "operationId": "conversation.tail", "tags": [ "messaging" ], "summary": "The log as it is owed to the caller", "description": "Live-messaging §9.3, from position `from`, at most 256 entries, held up to 8 s while there is nothing new.", "parameters": [ { "name": "from", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0 }, "description": "the first entry wanted (default 0)" }, { "name": "wait_ms", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 8000 }, "description": "how long to hold the request while the log has nothing past `from` (default and most 8000)" } ], "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "entries": { "type": "array", "items": { "type": "object" } }, "withheld": { "type": "array", "items": { "type": "integer" } }, "next": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/key-package": { "post": { "operationId": "conversation.import", "tags": [ "messaging" ], "summary": "A member's KeyPackage", "description": "Kept for the member's Add. A member's request: its grant (`Authorization: Bearer`, the record in base64url) and its proof (`WAI-Room-Proof`) over a challenge the service issued; every answer carries a fresh one in `WAI-Room-Challenge`. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "key_package": { "$ref": "#/components/schemas/Hex" } }, "required": [ "key_package" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/mls": { "post": { "operationId": "conversation.update", "tags": [ "messaging" ], "summary": "A GroupInfo, or a commit and its Welcome", "description": "Checked by the clock and carried; it then signs an epoch statement (live-messaging §7.3). A member's request: its grant (`Authorization: Bearer`, the record in base64url) and its proof (`WAI-Room-Proof`) over a challenge the service issued; every answer carries a fresh one in `WAI-Room-Challenge`. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "frames": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" }, "minItems": 1 } }, "required": [ "frames" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "the clock refuses it (its reason)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/post": { "post": { "operationId": "message.create", "tags": [ "messaging" ], "summary": "A message", "description": "Its sealed object and receipts, taken after the checks of live-messaging §9.2, once on stable storage. A member's request: its grant (`Authorization: Bearer`, the record in base64url) and its proof (`WAI-Room-Proof`) over a challenge the service issued; every answer carries a fresh one in `WAI-Room-Challenge`. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "type": "integer" }, "properties": { "$ref": "#/components/schemas/Hex" }, "payload": { "$ref": "#/components/schemas/Hex" }, "receipts": { "$ref": "#/components/schemas/Hex" } }, "required": [ "group", "properties", "payload", "receipts" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "refused by §9.2 (its reason)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/ack": { "post": { "operationId": "acknowledgement.create", "tags": [ "messaging" ], "summary": "An acknowledgement", "description": "The caller's own (live-messaging §9.1). A member's request: its grant (`Authorization: Bearer`, the record in base64url) and its proof (`WAI-Room-Proof`) over a challenge the service issued; every answer carries a fresh one in `WAI-Room-Challenge`. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "ack": { "$ref": "#/components/schemas/Hex" } }, "required": [ "ack" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`, or another member's acknowledgement", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "refused (its reason)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/leave": { "post": { "operationId": "conversation.leave", "tags": [ "messaging" ], "summary": "The caller leaves", "description": "The clock signs its `leave` (reason 1). A member's request: its grant (`Authorization: Bearer`, the record in base64url) and its proof (`WAI-Room-Proof`) over a challenge the service issued; every answer carries a fresh one in `WAI-Room-Challenge`. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "400": { "description": "a body that is not JSON", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "the service could not read its own record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/msg/remove": { "post": { "operationId": "member.delete", "tags": [ "messaging" ], "summary": "The owner removes a member", "description": "The clock signs its `leave` (reason 2). A member's request: its grant (`Authorization: Bearer`, the record in base64url) and its proof (`WAI-Room-Proof`) over a challenge the service issued; every answer carries a fresh one in `WAI-Room-Challenge`. The body is at most 128 KiB.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "principal": { "$ref": "#/components/schemas/Hex32" } }, "required": [ "principal" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "seq": { "type": "integer" }, "end": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "`not_a_member`, or `power_not_granted`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "`not_present`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "500": { "description": "the service could not read its own record", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "503": { "description": "the history store could not take it", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/call/challenge": { "get": { "operationId": "call.setup", "tags": [ "calls" ], "summary": "A challenge for a proof", "description": "In a call room (`[call]`); 404 in any other.", "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "challenge": { "$ref": "#/components/schemas/Hex32" } } } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/call/place": { "post": { "operationId": "call.create", "tags": [ "calls" ], "summary": "Place a call", "description": "A key proves itself (a proof naming no grant) and names its invitees; the authority signs the call descriptor (WCT1) for the room, and the caller's publisher grant (live-messaging §10). In a call room (`[call]`); 404 in any other.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "invitees": { "type": "array", "items": { "$ref": "#/components/schemas/Hex32" }, "minItems": 1 } }, "required": [ "invitees" ] } } } }, "security": [ { "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "`busy` (a call is in progress) or `full`", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/call/ring": { "post": { "operationId": "call.invite", "tags": [ "calls" ], "summary": "Ring invitees", "description": "The caller's invitations (WCI1), with its grant and proof; each is checked (§10.2) and held in its callee's inbox. In a call room (`[call]`); 404 in any other.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "invitations": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" }, "minItems": 1 } }, "required": [ "invitations" ] } } } }, "security": [ { "roomGrant": [], "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "type": "boolean" }, "rung": { "type": "integer" } } } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "not the caller", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "an invitation refused (its reason and `at`)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "no call in this room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/inbox": { "get": { "operationId": "inbox.show", "tags": [ "calls" ], "summary": "The caller's inbox", "description": "On a proof of the key (§10.4). In a call room (`[call]`); 404 in any other.", "security": [ { "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object", "properties": { "principal": { "$ref": "#/components/schemas/Hex32" }, "invitations": { "type": "array", "items": { "type": "object" } }, "now_us": { "type": "integer" }, "t0_us": { "type": "integer" } } } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/call/answer": { "post": { "operationId": "answer.create", "tags": [ "calls" ], "summary": "Accept, decline or cancel", "description": "A callee's accept or decline, or the caller's cancel (WCA1, §10.3); on an accept the callee's publisher grant. In a call room (`[call]`); 404 in any other.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "answer": { "$ref": "#/components/schemas/Hex" } }, "required": [ "answer" ] } } } }, "security": [ { "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "description": "the request does not read", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "an answer of another key, or a missed call (the clock's)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "this service keeps no such room, or nothing at that path in this service's mode", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "the answer refused (its reason)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "410": { "description": "no call in this room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "413": { "description": "the body is larger than the request takes", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/call": { "get": { "operationId": "call.show", "tags": [ "calls" ], "summary": "The current call", "description": "To a party to it, on a proof of its key: the call's records, which a party verifies offline (§10). In a call room (`[call]`); 404 in any other.", "security": [ { "roomProof": [] } ], "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { "type": "object" } } } }, "401": { "description": "no proof of possession that passes; a fresh challenge is in WAI-Room-Challenge", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "not a party to the call", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "no call, or not a call room", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "securitySchemes": { "roomGrant": { "type": "http", "scheme": "bearer", "description": "a grant of the room: its WRG1 record in base64url" }, "roomProof": { "type": "apiKey", "in": "header", "name": "WAI-Room-Proof", "description": "a proof of possession over the service's challenge (live-rooms §10.5.3), in base64url" } }, "schemas": { "Hex": { "type": "string", "pattern": "^([0-9a-fA-F]{2})*$", "description": "bytes in hexadecimal" }, "Hex32": { "type": "string", "pattern": "^[0-9a-f]{64}$", "description": "32 bytes, 64 lowercase hexadecimal digits" }, "Error": { "type": "object", "properties": { "error": { "type": "string", "description": "a reason code or a short statement of what is wrong" }, "reason": { "type": "string" }, "retry_after_s": { "type": "number" } }, "required": [ "error" ] }, "Room": { "type": "object", "properties": { "room": { "$ref": "#/components/schemas/Hex32" }, "generation": { "type": "integer" }, "t0_us": { "type": "integer" }, "now_us": { "type": "integer" }, "descriptor": { "$ref": "#/components/schemas/Hex", "description": "the signed WRD1 descriptor" }, "compose": { "$ref": "#/components/schemas/Hex", "description": "the signed WCD1 composition descriptor" }, "grants": { "type": "array", "items": { "$ref": "#/components/schemas/Hex" }, "description": "the publishers' WRG1 grants" }, "present": { "type": "integer" }, "publishers": { "type": "integer" }, "max_publishers": { "type": "integer" }, "e2ee": { "type": [ "object", "null" ] }, "remote": { "type": [ "object", "null" ] }, "age": { "type": [ "object", "null" ] }, "co": { "type": [ "object", "null" ] }, "watch": { "type": [ "object", "null" ] }, "world": { "type": [ "object", "null" ] } }, "required": [ "room", "t0_us", "descriptor", "grants" ] }, "GrantAnswer": { "type": "object", "properties": { "grant": { "$ref": "#/components/schemas/Hex", "description": "the WRG1 record" }, "digest": { "$ref": "#/components/schemas/Hex32" }, "exp": { "type": "integer" }, "role": { "type": "string" }, "room": { "$ref": "#/components/schemas/Room" } }, "required": [ "grant", "digest", "exp", "role", "room" ] } } } }