Skip to content

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.

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

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.

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

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

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

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

wire.MailboxType. MMA requests carry the string; the database stores the number.

Name Value String
MailboxPrivate 0 private
MailboxShared 1 shared
MailboxPublic 2 public

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

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

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
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.

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>}.

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.

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>}]}.

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

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.

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