Protocol reference

Every message type, every field, and the model underneath them. Worked examples live on the cookbook page.

Protocol model

Wire framing, per transport

TransportFraming
TCP4-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.
WebSocketOne JSON object per text frame. No manual length prefix - the WebSocket framing already delimits messages. Max 64KB per frame.
UDPOne JSON object per datagram, no framing needed (UDP already preserves message boundaries). Max 16KB per datagram - larger payloads must be chunked by the client.

Addressing and sessions

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.

The common envelope fields

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.

Message catalog

Client → relay

typefieldsdescription
registeraddr (string, optional if pubkey given)
pubkey (base64, optional)
Claim an address. See identity for the pubkey variant.
sendto (addr, required)
ct (string)
pub, nonce (optional, E2E convention)
id (string, optional)
Deliver to one address. Queued if offline.
ackto (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_pipeto (addr, required)Open a raw byte-stream tunnel to another currently-connected address. See cookbook.
pipe_datapipe_id (string)
any other fields - forwarded verbatim to the other side, this message type carries no fixed schema
Send on an open pipe.
close_pipepipe_id (string)Tear down a pipe you opened or were given.
find_matchgame (string, a free-form tag)Pair up with another waiting caller under the same tag.
join_roomroom (string)Join a named broadcast group.
room_sendroom (string)
ct (string)
Broadcast to every other member of a room.
subscribetopic (string, may contain +/#)Subscribe to a topic filter; replays matching retained messages.
unsubscribetopic (string, exact filter previously subscribed)Remove a subscription.
publishtopic (string, concrete, no wildcards)
ct (string)
retain (bool, optional)
Publish to a topic; optionally retained for future subscribers.

Relay → client

typefieldsdescription
registeredaddr
queued (int)
Registration succeeded. Any buffered backlog for this address was already flushed as deliver frames immediately before this one.
deliverfrom
ct, pub, nonce
id (if the sender set one)
kind
Incoming message from a send.
ackid
from
Delivery confirmation forwarded from a recipient.
queuedtoYour send target was offline; message buffered for later.
capabilitiestransports (object: name → port)
load_tier (string)
load_factor (float 0..1)
Response to a capabilities request.
online_countcount (int)Response to a list_online request - never an address list, just a count.
turn_credentialsurls (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_openpipe_id
peer
Confirms your own open_pipe request succeeded.
pipe_incomingpipe_id
peer
Someone opened a pipe to you - pushed unprompted, same pipe_id they got back.
pipe_datapipe_id
whatever fields the sender put in
Data forwarded from the other end of a pipe, verbatim.
pipe_closedpipe_idThe other side closed the pipe, or disconnected.
waitinggameNo match yet; you're queued.
matchedgame
peer (addr)
Paired with another caller - message them directly with send.
joined_roomroom
members (int)
Confirms join_room.
room_deliverroom
from
ct
Incoming room broadcast.
subscribed / unsubscribedtopicConfirms a subscription change.
messagetopic
from (null if replayed-retained)
ct
retained (bool)
Incoming pub/sub delivery, live or retained-replay.
errormsg (string)See the error reference below.

Error reference

error.msgcause
invalid addressempty, or over 256 characters
address in usesomeone else currently holds that exact address
invalid pubkeypubkey wasn't valid base64
alias bound to a different keysee self-certifying identity
relay overloaded, retry laterthe relay is under heavy load; try again shortly
register firstsent send/ack/subscribe/etc before a successful register
join a room firstsent room_send without a prior join_room
rate limit exceededper-connection message/byte budget exhausted - back off and retry
unknown message typethe "t" field didn't match anything in the catalog
invalid json(WebSocket only) the frame wasn't parseable JSON