Fetching content
Raw message and attachment bytes flow only through the authorized content lane of client-owned mail replication.
Events are thin change notifications: they identify messages and their mailbox occurrences but carry no metadata, content, or content handles (see Data model). Raw message and attachment bytes are served only through the content lane of client-owned mail replication (RFC 18), to a DUCS Consumer with an authorized, currently active mail-replication stream.
Gateway has exactly two content HTTP routes:
| Route | Purpose |
|---|---|
POST /gateway/content/v1 | Download: the bounded, framed content lane described below |
POST /gateway/content | Upload: staged RFC 5322 bytes for drafts and submissions |
Why not JSON-RPC
Content is the one place the API moves opaque bytes rather than a typed message. Putting a possibly multi-megabyte body into a JSON-RPC result would mean base64 (≈33% larger, buffered whole) inside the envelope. The content lane instead streams bounded binary frames, so neither Gateway nor the client needs to hold a complete body in memory. Supermail stores none of the bytes; Sync reads them from the provider for each request.
The content lane
POST /gateway/content/v1
Authorization: Bearer <access_token>
traummail-content-request-nonce: <32 bytes, hex>
traummail-content-request-digest: <32 bytes, hex>
<binary ContentRequestV1>The request body is one binary ContentRequestV1 of at most 4 KiB. It names:
- the protocol revision (
1) and a fresh 32-byte request nonce; - the mail-replication stream id;
- the Mail/1 message id and, for a single MIME part, an optional attachment id;
- the message's content locator: the opaque content reference and
content_revisionexactly as replicated in the Mail/1 relation.
The two headers must repeat the request's nonce and digest. The bearer must authorize the exact current active stream named in the request. Gateway checks that before it opens any provider read and keeps rechecking the authority while bytes flow; losing it ends the response without a result frame.
Use @traummail/gateway-client rather than building requests by hand: its
content facade opens the carrier, and @traummail/replication-sdk verifies the
frames. The V1 content lane is currently implemented for Gmail only.
Response
The response has Content-Type: application/vnd.traummail.content-v1,
Cache-Control: no-store, no-cache, private, and a traummail-content-open
header carrying the base64url-encoded open result:
| Status | Open result |
|---|---|
200 OK | Accepted. The body is a sequence of data frames followed by one result frame. |
400 Bad Request | Missing correlation headers, or refused as an invalid request (including a nonce or digest that does not match the body). |
403 Forbidden | Refused: the stream is not currently authorized for this bearer. |
429 Too Many Requests | Refused: capacity, with an optional retry-after in milliseconds. |
Each data frame carries at most 256 KiB, a response carries at most 8,192 data frames and 2 GiB in total, and one request may run for at most 30 minutes. The final result frame reports success or a failure reason (content changed, content unavailable, provider failure, object too large, or deadline exceeded) together with the data-frame count, total length, and body digest the client verifies. A response that ends without a result frame is incomplete and must be discarded.
Transport
The V1 lane carries provider plaintext and relies on TLS. It does not use DUCS
payload encryption. Public traffic must arrive through a trusted secure edge;
only direct loopback requests without forwarding headers may use cleartext.
Anything else is refused with 421 Misdirected Request.