Transports
Operations in this specification are defined without a reference to a transport. This page says how they travel.
Two concerns live here:
| What it is | Server state | |
|---|---|---|
| Delivery transport | How bytes reach a client that is connected right now | none - “a connection exists” is not a record |
| Push target | How a sleeping client is woken | durable, registered against one mailbox |
Conflating them is a mistake that should be avoided. A delivery transport leaves no residue, while a push target is the longest-lived state in the design and the only kind that involves a third party.
Serialization
Section titled “Serialization”Every binding carries the same canonical serialization: TLS presentation language as implemented by tls_codec, the same codec MLS uses.
- Protocol types MUST use
application/cryptid+tls - Operational metadata MAY use
application/json - No binding may re-encode an envelope;
encryption_blobpasses through unmodified
The baseline. Every operation has an HTTP binding, and a server MUST implement it.
- Requests carry PrivacyPass tokens as
Authorization: PrivateToken token="<base64url>"per RFC 9577 - Requests carry retrieval sessions as
Authorization: MailboxSession <token> - Errors arrive as an
ErrorEnvelopein the response body under the mapped status code
Delivery Transports
Section titled “Delivery Transports”Frame Format
Section titled “Frame Format”The streaming bindings share one frame format: a one-byte type tag followed by a tls_codec payload. Envelope bytes pass through from the queue without re-encoding.
| Tag | Frame | Direction | Payload |
|---|---|---|---|
0x01 |
Subscribe | client -> server | none; the session names the mailbox |
0x02 |
Unsubscribe | client -> server | none |
0x03 |
Ack | client -> server | message_ids |
0x04 |
Ping | client -> server | timestamp |
0x10 |
Envelope | server -> client | QueuedEnvelope |
0x11 |
Status | server -> client | connection state |
0x12 |
Error | server -> client | ErrorEnvelope |
0x13 |
Pong | server -> client | echoed timestamp |
- Implementations MUST ignore unknown frame types rather than closing the connection
- A frame carrying an
ErrorEnvelopeMUST use the same codes as the HTTP binding
WebTransport
Section titled “WebTransport”The primary foreground binding.
https://{server}/api/v1/envelopes/subscribeWebTransport connection setup is 1-RTT cold and 0-RTT on resumption against roughly three for WebSocket, and that connection survives a network change rather than dying on a WiFi-to-cellular handoff. This is important when we’re holding one connection per mailbox.
- Clients MUST open one connection per mailbox
- Clients SHOULD use 0-RTT resumption where available
- Servers MUST authorize each connection with a retrieval session for the mailbox it covers
WebSocket
Section titled “WebSocket”This is fallback for when QUIC is unreachable. QUIC runs over UDP and that gets blocked on a lot of corporate networks and some carriers.
GET /api/v1/envelopes/subscribeUpgrade: websocketIdentical frame format and semantics. The only difference is the handshake cost and that a network change forces a reconnect.
- Clients SHOULD attempt WebTransport first and fall back to WebSocket only on failure to establish
- Clients MUST NOT interpret a WebSocket fallback as a signal about server capability beyond reachability
For clients that can hold no connection at all.
Polling is FetchEnvelopes on a timer. It needs no binding of its own.
- Clients SHOULD poll no more often than the interval the server advertises
- Clients SHOULD add jitter, so a population of pollers does not synchronize
Connection Limits
Section titled “Connection Limits”Servers MAY limit concurrent delivery connections per source address, as resource protection.
Push Targets
Section titled “Push Targets”A push target is registered against exactly one mailbox and stored until revoked. It is durable in a design where almost nothing else is.
Requirements
Section titled “Requirements”- Wakeups MUST be contentless
- Wakeups MUST NOT name the mailbox that received a message
- Clients SHOULD register distinct endpoints per mailbox where the distributor permits it
- Servers MUST discard a push target when its mailbox is deregistered
UnifiedPush
Section titled “UnifiedPush”Self-hostable, and the only push target a self-hosted server can use unaided. The endpoint is user-supplied: ntfy, gotify, or any other UnifiedPush distributor.
The server POSTs a contentless body to the endpoint. The client wakes, opens a retrieval session, fetches, and sleeps.
Gateway
Section titled “Gateway”APNs and FCM cannot be used directly by a self-hosted server, because both require the credentials of whoever ships the client application rather than whoever runs the server. A gateway holds those credentials and forwards to the platform; the servre only ever sees a URL and a token.
This introduces a centralized dependency into a federated protocol. It is a compromise made for platform reach, not a design goal, and a deployment that can use UnifiedPush should.