Every message type, every field, and the model underneath them. Worked examples live on the cookbook page.
| Transport | Framing |
|---|---|
| TCP | 4-byte big-endian unsigned length, followed by exactly that many bytes of UTF-8 JSON. One frame per message, both directions. Max 64KB per frame. |
| WebSocket | One JSON object per text frame. No manual length prefix - the WebSocket framing already delimits messages. Max 64KB per frame. |
| UDP | One JSON object per datagram, no framing needed (UDP already preserves message boundaries). Max 16KB per datagram - larger payloads must be chunked by the client. |
An address is any string up to 256 characters, free of charge, first-come-first-served for the lifetime of that connection. There is one flat namespace shared across all transports - a TCP client and a WebSocket client can address each other directly. An address is released the instant its connection closes (modulo store-and-forward buffering for messages sent to it while briefly offline, see cookbook) and can then be claimed by anyone.
Addresses are either freely chosen strings, or derived from a public key for a self-certifying identity.
Every message in both directions is a flat JSON object with a "t" field naming its type. Beyond that, the relay is content-agnostic: the "ct" field that appears on most message types is an opaque string as far as the relay is concerned - it is never parsed, decrypted, validated, or logged by content. What you put there and how you encode it (plaintext, base64 ciphertext, a JSON-encoded sub-object) is entirely up to your client; the encryption cookbook entry describes the convention this relay's own client library and demos use.
Client → relay
| type | fields | description |
|---|---|---|
| register | addr (string, optional if pubkey given) pubkey (base64, optional) | Claim an address. See identity for the pubkey variant. |
| send | to (addr, required) ct (string) pub, nonce (optional, E2E convention) id (string, optional) | Deliver to one address. Queued if offline. |
| ack | to (addr) id (string) | Confirm receipt of a send that had an id. |
| capabilities | (none) | Ask what the relay currently supports and its load state. |
| list_online | (none) | Ask how many addresses are registered relay-wide right now. See cookbook. |
| turn_credentials | (none) | Mint short-lived TURN credentials for WebRTC's iceServers. See cookbook. |
| open_pipe | to (addr, required) | Open a raw byte-stream tunnel to another currently-connected address. See cookbook. |
| pipe_data | pipe_id (string) any other fields - forwarded verbatim to the other side, this message type carries no fixed schema | Send on an open pipe. |
| close_pipe | pipe_id (string) | Tear down a pipe you opened or were given. |
| find_match | game (string, a free-form tag) | Pair up with another waiting caller under the same tag. |
| join_room | room (string) | Join a named broadcast group. |
| room_send | room (string) ct (string) | Broadcast to every other member of a room. |
| subscribe | topic (string, may contain +/#) | Subscribe to a topic filter; replays matching retained messages. |
| unsubscribe | topic (string, exact filter previously subscribed) | Remove a subscription. |
| publish | topic (string, concrete, no wildcards) ct (string) retain (bool, optional) | Publish to a topic; optionally retained for future subscribers. |
Relay → client
| type | fields | description |
|---|---|---|
| registered | addr queued (int) | Registration succeeded. Any buffered backlog for this address was already flushed as deliver frames immediately before this one. |
| deliver | from ct, pub, nonce id (if the sender set one) kind | Incoming message from a send. |
| ack | id from | Delivery confirmation forwarded from a recipient. |
| queued | to | Your send target was offline; message buffered for later. |
| capabilities | transports (object: name → port) load_tier (string) load_factor (float 0..1) | Response to a capabilities request. |
| online_count | count (int) | Response to a list_online request - never an address list, just a count. |
| turn_credentials | urls (array) username, credential (strings, or both null if no TURN server is configured) ttl (int, seconds) | Response to a turn_credentials request. Expires after ttl - request a fresh one if a call runs longer. |
| pipe_open | pipe_id peer | Confirms your own open_pipe request succeeded. |
| pipe_incoming | pipe_id peer | Someone opened a pipe to you - pushed unprompted, same pipe_id they got back. |
| pipe_data | pipe_id whatever fields the sender put in | Data forwarded from the other end of a pipe, verbatim. |
| pipe_closed | pipe_id | The other side closed the pipe, or disconnected. |
| waiting | game | No match yet; you're queued. |
| matched | game peer (addr) | Paired with another caller - message them directly with send. |
| joined_room | room members (int) | Confirms join_room. |
| room_deliver | room from ct | Incoming room broadcast. |
| subscribed / unsubscribed | topic | Confirms a subscription change. |
| message | topic from (null if replayed-retained) ct retained (bool) | Incoming pub/sub delivery, live or retained-replay. |
| error | msg (string) | See the error reference below. |
| error.msg | cause |
|---|---|
| invalid address | empty, or over 256 characters |
| address in use | someone else currently holds that exact address |
| invalid pubkey | pubkey wasn't valid base64 |
| alias bound to a different key | see self-certifying identity |
| relay overloaded, retry later | the relay is under heavy load; try again shortly |
| register first | sent send/ack/subscribe/etc before a successful register |
| join a room first | sent room_send without a prior join_room |
| rate limit exceeded | per-connection message/byte budget exhausted - back off and retry |
| unknown message type | the "t" field didn't match anything in the catalog |
| invalid json | (WebSocket only) the frame wasn't parseable JSON |