Wire format & status codes
This page lists the bytes a go-ricochet 1.1.0 server reads and writes on each protocol stream. The Go names come from pkg/wire; the JSON field names come from the handlers under internal/protocol. Protocol IDs are on Protocol IDs.
Framing
Section titled “Framing”Every request and every response is one length-prefixed frame. Encoding is JSON (UTF-8); there is no protobuf.
| Part | Size | Content |
|---|---|---|
| Length | 4 bytes | Big-endian uint32: the number of payload bytes that follow |
| Payload | Length bytes | One JSON object, except the MAA retrieve response (below) |
| Rule | Value |
|---|---|
| Maximum frame payload | 10 MiB (10,485,760 bytes); a larger frame is refused on read and on write |
| Frames per stream | One request, then one response; the server closes the stream |
| Binary data in JSON | Standard base64 with padding (payload, body, content) |
| Idle stall limit | performance.connection_timeout_sec (default 30 s) without bytes moving drops the stream |
| Work deadline | performance.message_timeout_sec (default 10 s) from the decoded request to the response |
MAA retrieve response
Section titled “MAA retrieve response”The retrieve response is one frame whose payload is itself a sequence of length-prefixed parts.
| Part | Content |
|---|---|
| 4-byte big-endian length, then JSON | {"messageCount": <int>, "hasMore": <bool>} |
Repeated messageCount times: 4-byte big-endian length, then JSON |
One message |
A page holds at most 10 MiB minus 64 KiB of encoded messages.
Message
Section titled “Message”The message envelope (wire.Message) is the request body on /sf-network/submit/1.0.0 and each element of a batch or a retrieve page.
| JSON field | Type | Meaning |
|---|---|---|
messageId |
string | Client-chosen ID; the Go client uses a UUID |
recipientPeerId |
string | Mailbox owner, a base58 peer ID |
senderPeerId |
string | Must equal the connection’s peer ID, unless the connection is a trusted forwarder |
payload |
string | Base64 of the payload bytes; at most 10 MiB decoded |
priority |
int | Priority, 0 to 3 |
expiryTimestamp |
int | Unix milliseconds; a message already expired is refused; 0, or a time past the mailbox’s retention, is set to now plus the mailbox’s retention_days |
hopCount |
int | Forwarding hops; refused above 10 |
flags |
int | Protocol flags bitmap |
createdTimestamp |
int | Unix milliseconds |
folderPath |
string, optional | Target folder; empty means inbox |
sequenceNumber |
int, optional | Set by the server: position in the mailbox |
messageFlags |
int, optional | IMAP-style flags bitmap |
persistent |
bool, optional | Kept after acknowledgement (marked seen) instead of removed |
Protocol flags
Section titled “Protocol flags”wire.SFMessageFlags, the flags field. The server stores it and sets FlagForwarded on a forwarded submission; it acts on no other bit.
| Name | Bit | Value |
|---|---|---|
FlagEncrypted |
0 | 0x01 |
FlagSigned |
1 | 0x02 |
FlagCompressed |
2 | 0x04 |
FlagRequireAck |
3 | 0x08 |
FlagForwarded |
4 | 0x10 |
FlagNone |
none | 0x00 |
Message flags
Section titled “Message flags”wire.MessageFlags, the messageFlags field and the addFlags / removeFlags of updateFlags.
| Name | Bit | Value |
|---|---|---|
MsgFlagSeen |
0 | 0x01 |
MsgFlagFlagged |
1 | 0x02 |
MsgFlagDeleted |
2 | 0x04 |
MsgFlagDraft |
3 | 0x08 |
MsgFlagNone |
none | 0x00 |
Priorities
Section titled “Priorities”wire.MessagePriority, the priority field and minPriority of a retrieve.
| Name | Value | String |
|---|---|---|
PriorityLow |
0 | low |
PriorityNormal |
1 | normal |
PriorityHigh |
2 | high |
PriorityUrgent |
3 | urgent |
Mailbox types
Section titled “Mailbox types”wire.MailboxType. MMA requests carry the string; the database stores the number.
| Name | Value | String |
|---|---|---|
MailboxPrivate |
0 | private |
MailboxShared |
1 | shared |
MailboxPublic |
2 | public |
Access modes
Section titled “Access modes”wire.AccessMode, the accessMode of grantAccess. The server parses the string case-insensitively and also accepts read_only, write_only and read_write.
| Name | Value | String |
|---|---|---|
AccessReadOnly |
0 | readOnly |
AccessWriteOnly |
1 | writeOnly |
AccessReadWrite |
2 | readWrite |
Visibility
Section titled “Visibility”wire.Visibility, for documents, feeds and collections. Requests carry the string (case-insensitive).
| Name | Value | String | Default for |
|---|---|---|---|
VisibilityPrivate |
0 | private |
Documents, collections |
VisibilityShared |
1 | shared |
None |
VisibilityPublic |
2 | public |
Feeds |
Message type codes
Section titled “Message type codes”wire.SFMessageType defines these codes. No frame in 1.1.0 carries them: the protocol ID and the operationType or operation field select the operation.
| Name | Value |
|---|---|
MsgTypeStoreMessage |
0x30 |
MsgTypeStoreAck |
0x31 |
MsgTypeRetrieveMessages |
0x32 |
MsgTypeRetrieveResp |
0x33 |
MsgTypeMarkDelivered |
0x34 |
MsgTypeMarkDeliveredAck |
0x35 |
MsgTypeForwardMessage |
0x36 |
MsgTypeForwardAck |
0x37 |
MsgTypeQueryCapacity |
0x38 |
MsgTypeCapacityResp |
0x39 |
MsgTypeSetPriority |
0x3A |
MsgTypeSetExpiry |
0x3B |
MsgTypeUpdateFlags |
0x3C |
MsgTypeUpdateFlagsAck |
0x3D |
MsgTypeExpunge |
0x3E |
MsgTypeExpungeAck |
0x3F |
MsgTypeDeleteMessages |
0x40 |
MsgTypeDeleteMsgsAck |
0x41 |
MSA: submit
Section titled “MSA: submit”| Direction | Shape |
|---|---|
| Request, single | A message |
| Request, batch | {"messages": [<message>, ...]}, 1 to 100 messages |
| Response, single | StoreAck |
| Response, batch | {"acks": [<StoreAck>, ...], "accepted": <int>, "total": <int>}, acks in request order |
StoreAck fields: messageId, success, errorMessage (optional), estimatedDeliveryTime (optional), status (optional), retryAfterMs (optional). A batch refused as a whole answers with one ack and total 0.
MAA: access
Section titled “MAA: access”operationType |
Request fields | Response fields |
|---|---|---|
retrieve (or absent) |
peerId (mailbox owner), folderPath, fromSequence, maxMessages, minPriority |
Compound frame |
markDelivered |
messageIds, folderPath |
success, updatedCount, errorMessage |
updateFlags |
messageId, addFlags, removeFlags |
success, newFlags, errorMessage |
expunge |
peerId (must be the caller or empty), folderPath (empty means every mailbox) |
success, deletedCount, errorMessage |
deleteMessages |
messageIds |
success, deletedCount, errorMessage |
An MAA request refused before its handler runs answers {"error": <string>, "status": <int>, "retryAfterMs": <int>}.
MMA: admin
Section titled “MMA: admin”Every request carries operationType. Response: success, errorMessage, status, retryAfterMs, plus the field named below.
operationType |
Request fields | Extra response field |
|---|---|---|
createMailbox |
ownerPeerId, folderPath, mailboxType, maxMessages, retentionDays, retentionCount |
None |
deleteMailbox |
ownerPeerId, folderPath |
None |
grantAccess |
ownerPeerId, folderPath, granteePeerId, accessMode |
None |
revokeAccess |
ownerPeerId, folderPath, granteePeerId |
None |
listACL |
ownerPeerId, folderPath |
acl: [{peerId, accessMode, grantedAt}] |
listMailboxes |
ownerPeerId |
mailboxes: [{folderPath, type, maxMessages, retentionDays, createdAt}] |
updateConfig |
folderPath, maxMessages, retentionDays, retentionCount |
None |
queryCapacity |
None | capacity: {totalStorageBytes, usedStorageBytes, availableStorageBytes, messageCount, activeMailboxes, healthScore, sampledAt} |
getMailboxInfo |
folderPath |
data: {address, type, messageCount, createdAt, lastAccessedAt, maxMessages, retentionDays, retentionCount} |
MMA also accepts address ("<peerId>/<folderPath>"), type and targetPeerId as aliases of folderPath, mailboxType and granteePeerId.
Store protocols: SDA, SFA, SCA
Section titled “Store protocols: SDA, SFA, SCA”The three store protocols share one envelope.
| Direction | Fields |
|---|---|
| Request | operation, ownerPeerId, path, headers (string map), body (base64), plus the operation’s fields below |
| Response | status (int), headers (map), body (base64); SCA adds data (raw JSON) for item GET and QUERY |
Paths match ^[a-zA-Z0-9\-_/]+$, are at most 256 bytes, must not start with / and must not contain ...
| Protocol | Operation-specific request fields |
|---|---|
| SDA | maxVersions, versionNumber (HISTORY); batchDocuments: [{path, body, contentType, ifMatch, visibility}], at most 100 and 6 MiB decoded (BATCH_PUT); listCursor, listLimit (LIST); directoryAction, directoryQuery, directoryCursor, directoryLimit (DIRECTORY); visibility; accessAction, readerPeerId (ACCESS) |
| SFA | title, description, collaborative, visibility (CREATE); entryType (APPEND); sequenceNumber, fromSequence, toSequence, limit (GET); batchQueries: [{ownerPeerId, path, fromSequence, limit}] (BATCH_GET); accessAction, readerPeerId (ACCESS) |
| SCA | key; name, visibility (CREATE); filter, sortField, sortAsc, limit, offset, cursor, wantTotal (QUERY, LIST); accessAction, readerPeerId (ACCESS) |
| Request header | Protocol | Meaning |
|---|---|---|
Content-Type |
SDA PUT |
Document content type, at most 256 bytes |
If-Match |
SDA PUT, PATCH; SCA PUT |
Write only if the current ETag matches; else 409 |
If-None-Match |
SDA GET, HEAD |
Answer 304 if the ETag matches |
| Response header | Protocol | Meaning |
|---|---|---|
Error |
All | Client-facing reason for a non-2xx status |
Retry-After-Ms |
All | Wait hint in milliseconds on a 429 or 503 |
ETag |
SDA, SFA, SCA | Content hash of the stored version |
Content-Type |
SDA, SFA, SCA | Content type of body |
Last-Modified |
SDA | Unix milliseconds (GET, PUT, PATCH); RFC 3339 string (HEAD) |
Version |
SDA | Version number |
Visibility |
SDA, SFA, SCA | private, shared or public |
Content-Length |
SDA HEAD |
Size in bytes |
Expected-ETag, Actual-ETag |
SDA | On a 409, the ETag sent and the ETag stored |
Has-More, Next-Cursor |
SDA LIST; SCA (Next-Cursor) |
Pagination |
Applied, Total |
SDA BATCH_PUT |
Documents written and documents sent |
Created-At |
SDA HISTORY, SFA, SCA |
Creation time |
X-Sequence, X-Next-Sequence, X-Has-More, X-Entry-Type, X-Created-By |
SFA | Entry sequence, next sequence, pagination, entry type, author |
X-Version, X-Record-Count, X-Total-Count, X-Has-More, Updated-At |
SCA | Item version, collection size, match count, pagination, update time |
The ACCESS operation answers a body of {"visibility": <string>, "readers": [{"peerId": <string>, "grantedAt": <unix ms>}]}.
Notification
Section titled “Notification”wire.Notification. The server writes it as one frame on /ricochet/mailbox-notify/1.0.0 (private mailboxes) or publishes the same JSON, unframed, on the mailbox’s GossipSub topic (shared and public mailboxes).
| JSON field | Type | Meaning |
|---|---|---|
mailboxPath |
string | Folder path |
mailboxType |
string | private, shared or public |
messageCount |
int | New messages; the server sends 1 per delivery |
timestamp |
int | Unix seconds |
serverPeerId |
string | The server that holds the message |
Status codes
Section titled “Status codes”pkg/wire defines the codes a client can name. The store handlers also use 201, 204, 304 and 413.
| Code | Go name | Meaning |
|---|---|---|
| 200 | StatusOK |
Success |
| 201 | none | Created (store PUT, CREATE, APPEND) |
| 204 | none | Deleted, or left the directory |
| 207 | StatusMultiStatus |
Batch with mixed outcomes; read the per-item results |
| 304 | none | Not modified (If-None-Match matched) |
| 400 | StatusBadRequest |
Malformed request, bad field, bad path, unknown operation |
| 403 | StatusForbidden |
Not the owner, not a reader, sender is not the connected peer |
| 404 | StatusNotFound |
No such mailbox, document, feed, collection or listing |
| 409 | StatusConflict |
ETag precondition failed |
| 413 | none | Document or batch over its size limit (SDA) |
| 429 | StatusTooManyRequests |
Per-peer rate limit; comes with a retry hint when known |
| 500 | StatusInternalError |
Server fault; the text is a fixed phrase, the detail is in the server log |
| 503 | StatusServiceUnavailable |
Admission control shed the request, the server is draining, or capacity is not sampled yet |
| 507 | StatusInsufficientStorage |
Mailbox full, quota reached or server over its storage budget; retrying does not help |
StoreAck.status is 0 from a server too old to classify; StatusCode() then reads 200 for success and 500 for failure. The mailbox protocols carry the retry hint as retryAfterMs; the store protocols carry it as the Retry-After-Ms header. See Errors for the Go client’s typed errors.
Field limits
Section titled “Field limits”| Constant | Bytes | Field |
|---|---|---|
MaxContentTypeLength |
256 | Document Content-Type |
MaxCollectionNameLength |
256 | Collection name |
MaxCollectionKeyLength |
256 | Collection item key |
MaxFeedTitleLength |
256 | Feed title |
MaxFeedDescriptionLength |
4096 | Feed description |
MaxEntryTypeLength |
64 | Feed entry entryType |
MaxDisplayNameLength |
256 | Directory displayName |
MaxBioLength |
4096 | Directory bio |
MaxAvatarHashLength |
256 | Directory avatarHash |
MaxDirectoryExtrasBytes |
4096 | Directory extras, as JSON |
MaxPayloadSize |
10,485,760 | Message payload |
MaxHopCount |
10 hops | Message hopCount |