QMail Services (Group 6)
The RAIDA messaging surface. Five commands cover the full lifecycle: upload encrypted file fragments, notify the recipient’s beacon RAIDA, poll or peek the beacon for new mail, and download the stored fragments.
Overview
QMail is RAIDA’s store-and-forward messaging service. A message is split into stripes (typically 7 data + 1 parity) and one stripe is uploaded to each of 8 RAIDA servers. The sender then notifies the recipient’s “beacon” RAIDA — a single RAIDA agreed in advance with the recipient — that mail is waiting. The recipient long-polls (ping) or non-blocking-checks (peek) the beacon, and on notification downloads the stripes from the storage RAIDAs and reassembles the message locally.
Current QMail commands are wrapped in the standard 32-byte RAIDA AES-128 packet header. The 48-byte QMail preamble documented below is not that packet header; it is the first 48 bytes of the decrypted request body after the RAIDA header has been parsed.
All five commands are encrypted at the wire level using AES-128 keyed by the caller’s selected coin AN. For upload, tell, ping, and peek, the handler also compares the preamble AN against the stored coin record identified by the preamble. Download currently relies on wire decryption and file/ACL checks.
End-to-end flow
Sender Storage RAIDAs (x8) Beacon RAIDA Recipient
| | | |
|-- upload (cmd 70) ----->| one per RAIDA, parallel | |
|<-- 250 OK --------------| | |
| | | |
|-------------------- tell (cmd 71) --------------------->| |
|<------------------- 250 OK -----------------------------| |
| | |
| |<-- ping (cmd 72) -------|
| | long-poll, parks fd |
| | |
| |-- 250 + tell blob ----->|
| | |
| |<------------------------- download (cmd 74) -----------|
| | one per RAIDA, parallel |
| |-- 250 + 256KB page ------------------------------------>|
| (reassemble locally)
Command reference
Each command has its own page documenting the wire format byte-by-byte, status codes, server-side behavior, reserved fields, and example request/response layouts.
| Code | Name | Purpose | Server function |
|---|---|---|---|
| 70 | upload | Store one stripe of one file under a 16-byte GUID. | cmd_qmail_upload2 |
| 71 | tell | Notify a recipient’s beacon RAIDA that mail is waiting. | cmd_qmail_tell2 |
| 72 | ping | Long-poll the beacon for new tell notifications. | cmd_qmail_ping2 |
| 73 | peek | Non-blocking check for tells newer than a timestamp. | cmd_qmail_peek2 |
| 74 | download | Fetch one 256 KB page of a stored file. | cmd_qmail_download2 |
Naming note
The on-disk server function names still carry a 2 suffix (cmd_qmail_upload2, cmd_qmail_tell2, etc.) for historical reasons — the original v1 commands at codes 60–64 have been removed. The active command codes are 70–74 and are what this documentation refers to as upload, tell, ping, peek, and download. If you see 2-suffixed names in raidax source, they are the same commands.
Universal preamble (48 bytes)
Every QMail request body begins with a 48-byte preamble that authenticates the caller/mailbox identity. The preamble is the same for all five commands and is separate from the RAIDA packet header. Its layout matches qmail_preamble_t in the raidax source:
| Offset | Size | Field | Description |
|---|---|---|---|
| 0–15 | 16 | Challenge | 12 random bytes followed by a 4-byte big-endian CRC32 of the random bytes. Required for replay protection. |
| 16–23 | 8 | Session ID | Set to all zeros for the standard QMail encryption mode. |
| 24–25 | 2 | Coin Type | Fixed 00 06 — the QMail/CloudCoin network ID. |
| 26 | 1 | Denomination | Caller/mailbox coin denomination (signed; valid range −8 to +6). |
| 27–30 | 4 | Serial Number | Caller/mailbox coin serial number (big-endian). Identifies the AN-bearer being authenticated. |
| 31 | 1 | Reserved | Was Device ID. Server reads and ignores — available for future use. Set to 0. |
| 32–47 | 16 | Authenticity (AN) | Caller/mailbox 16-byte AN for this RAIDA. Upload, tell, ping, and peek compare this to the stored AN for the (denom, SN) and reject with ERROR_INVALID_AN on mismatch. |
Status codes
QMail uses the standard RAIDA status enum from protocol.h. The codes any QMail handler can return:
| Decimal | Hex | Symbol | Meaning |
|---|---|---|---|
| 8 | 0x08 | ERROR_COIN_NOT_FOUND | The (denom, SN) in the preamble is not loaded on this RAIDA. |
| 16 | 0x10 | ERROR_INVALID_PACKET_LENGTH | Body too short for the command, missing terminator, or declared payload size doesn’t match the body length. |
| 18 | 0x12 | ERROR_WRONG_RAIDA | For tell: no recipient inbox could be written (zero deliveries). |
| 34 | 0x22 | ERROR_INVALID_ENCRYPTION | Wire-level decryption failed before the command body was reached. Most often caused by sending a zero-AN preamble or selecting an encryption coin the RAIDA doesn’t hold. |
| 40 | 0x28 | ERROR_INVALID_SN_OR_DENOMINATION | Denomination outside the allowed −8…+6 range. |
| 169 | 0xA9 | ERROR_PAYMENT_REQUIRED | Storage locker payment could not be consumed (empty key or insufficient balance). |
| 194 | 0xC2 | ERROR_FILESYSTEM | Server failed to create a directory, write a file, or arm an inotify watch. |
| 198 | 0xC6 | ERROR_INVALID_PARAMETER | Field validation failed (bad coin type, timestamp out of range, stripe count out of range, page beyond end of file, etc.). |
| 200 | 0xC8 | ERROR_INVALID_AN | Preamble AN does not match the server’s stored AN for the (denom, SN). |
| 202 | 0xCA | ERROR_FILE_NOT_EXIST | For download: the requested (GUID, file_type) is not stored. |
| 250 | 0xFA | STATUS_SUCCESS | Command completed successfully. |
| 254 | 0xFE | ERROR_MEMORY_ALLOC | Server allocation failure. |
Reserved bytes available for protocol extension
The current QMail wire structures contain the following reserved fields. Each is currently zero-filled by the sender and ignored by the server. They are catalogued here so future protocol extensions know which slots are available without breaking the existing layouts.
| Structure | Field | Bytes | Notes |
|---|---|---|---|
| Universal preamble | byte 31 (was Device ID) | 1 | Per-request, all five commands. Server reads but ignores. |
tell routing header (qmail_v2_tell_req_t) | reserved_1[4] at offset 20–23 | 4 | Sits between total_file_size and client_timestamp. |
| tell routing header | reserved_2[1] at offset 47 | 1 | Tail of the 48-byte routing header. |
tell recipient entry (qmail_v2_address_entry_t) | reserved[8] at offset 24–31 | 8 per recipient | One entry per recipient (To/CC/BCC). Multiplies with recipient count. |
tell file header (qmail_v2_file_header_t) | reserved[13] at offset 51–63 | 13 per email | Pass-through blob shared by tell → ping → download. Recipient sees these bytes verbatim. |
download request (qmail_download2_req_t) | bytes_per_page at offset 32 | 1 | Server hard-codes 256 KB pages and ignores this byte. |
ping/peek response array header (qmail_response_array_header_t) | reserved[5] at offset 3–7 | 5 | Sits between total_tells and the first 64-byte file-header that follows. |
The largest contiguous reserved region is qmail_v2_file_header_t.reserved[13], because it is part of the pass-through blob that the sender writes once and the recipient reads back through the tell → ping → download chain. That makes it the natural place to add per-email metadata (file-type masks, attachment counts, future ACL fields).