Protocol specification and TypeScript implementation for messages exchanged in the pigeon-room ecosystem.
This document is the human-readable source of truth for the wire format. Other-language implementations (Swift, etc.) should follow this spec.
pigeon-message defines two wire formats:
- Text message — UTF-8 JSON, sent as a WebSocket text frame. Used for signaling
(
init,clientOpen/clientClose,ping/pong) and user messages. - Binary message — a WebSocket binary frame: a JSON header plus a raw payload. Used for file/blob transfer.
The format is versioned independently from the pigeon-room and pigeon-link packages.
The format version is an integer. The current version is 1.
The spec defines the envelope (protocol mechanics); it never interprets application content.
| Concern | Owner | Fields |
|---|---|---|
| Versioning | spec | ver (text) · ver byte (binary) |
| Routing | spec | to, from, address |
| Ordering | spec | timestamp |
| Payload reconstruction (hint) | spec | payloadMeta |
| Application dispatch | app | type |
| Application meaning | app | body |
typeis a free string chosen by the application. Note that pigeon-room usesinit,ping,pong,clientOpen,clientClose,message, sent withfrom: host.bodyis opaque (unknown) in both frame types. Its meaning is decided by the application based ontype.
| Field | Type | Set by | Notes |
|---|---|---|---|
ver |
number |
sender | Format version. Text only. Absent ⇒ treated as v0. |
type |
string |
sender | Application-defined. |
to |
string[] |
sender | Recipient client IDs, or reserved words all / others / host. |
body |
unknown |
sender | Any valid JSON value. Opaque to the spec. |
from |
string |
room | Sender client ID, or host. |
address |
string |
room | The room the message belongs to. |
timestamp |
number |
room | Time of relay. Unix milliseconds. |
payloadMeta |
object |
sender | Binary only, optional. Describes the payload. |
On send, a client only provides type, to, body (plus ver, and payloadMeta for
binary). The room sets from, address, and timestamp authoritatively on relay.
all, others, and host are reserved. A client ID must never equal one of these.
all— everyone.others— everyone except the sender.host— the pigeon-room host (the server). Into, it addresses the host directly: the message reaches the host and is not relayed to other clients. Infrom, it marks host-originated messages.
A UTF-8 JSON string sent as a WebSocket text frame.
{
ver: number; // format version (currently 1)
type: string;
to: string[];
body: unknown;
from?: string; // set by room
address?: string; // set by room
timestamp?: number; // set by room
}Example (init, as sent by the room):
A WebSocket binary frame. Big-endian.
| ver(1B) | hdrLen(2B, BE) | header (UTF-8 JSON, hdrLen B) | payload |
ver— format version byte (currently0x01). This is the single source of truth for the binary frame's version.hdrLen— byte length of the JSON header. Max 65535 (2 bytes). Builders MUST throw if the header exceeds this; parsers MUST validate3 + hdrLen <= frame length.header— JSON with the same fields as a text message, plus optionalpayloadMeta. The header JSON does not carry averfield — the leadingverbyte holds the version. If averfield appears anyway, theverbyte takes precedence.payload— raw bytes (a single blob). The frame is self-delimiting: the payload runs from offset3 + hdrLento the end of the frame.
Optional, application-defined content describing the payload. Not needed to parse the frame.
example:
"payloadMeta": {
"name": "photo.png", // filename; not recoverable from the bytes
"mimeType": "image/png" // content type; some formats can't be sniffed (.js vs .txt)
}The format version is an integer embedded in the wire format:
- Text: the
verfield. - Binary: the leading
verbyte.
Rules:
- Within a version, new optional fields may be added. Parsers MUST ignore unknown fields.
- A version bump means the wire skeleton changed (e.g. multiple payloads, wider length fields). Parsers branch on the version.
verabsent ⇒ v0. Parsers must not reject; treat as v0. (This applies to text only; binary frames have always carried averbyte.)pigeon-roomandpigeon-linkdeclare a supported range of format versions (e.g. a given release supports message v0–v1).
pigeon-message/
README.md # this spec (source of truth)
index.ts # package exports
types/
text.ts # text message types and constants
binary.ts # binary frame types and constants
This package defines the format (spec + types). Encoding/decoding is the
responsibility of implementations (pigeon-room, pigeon-link).
deno check index.ts
{ "ver": 1, "type": "init", "to": ["id-0"], "body": { "id": "id-0", "clients": ["id-0", "id-1"] }, "from": "host", "address": "pipopo", "timestamp": 1690352186179 }