QMail Upload Large Page — Group 6, Code 75

Stores one numbered page of one striped QMail file object. This compatibility command is additive: legacy upload command 70 remains the small-file path, while command 75 stores paged attachments that are read back through download command 74. Commands 76 through 84 belong to the separate Object Transfer v1 subsystem.

Quick reference

Command Group6 (CHAT/QMail)
Command Code75
Relationship to cmd 70Additive large-page upload path; command 70 remains valid for small one-shot stripes.
Page data maximum256 KiB per request body page.
Page number16-bit unsigned, per server-side stored object, pages 0..65535.
Billing unitRound stored data up to 1 MiB units.
TransportTCP for Phase 1 large uploads. UDP may be used later for small pages if explicitly enabled.
EncryptionRequired. Same QMail preamble and AES-128 packet encryption model as command 70.

Purpose

Command 70 carries an entire stripe in one request and is limited by the legacy request body-size field. Command 75 allows the sender to split each server-side QMail object into fixed-size pages. Each page is uploaded independently and stored under the same email GUID, file type, and page number.

The client still does the striping, retry tracking, page accounting, and final message metadata. The server stores pages idempotently and does not maintain an upload session.

Download path

Command 75 only writes pages. The receiver fetches those pages with the existing command 74 download request. The command 74 server handler must check the per-page filename first and only then fall back to the legacy single-file seek path.

For command 75, the RAIDA request header is interpreted by command group and command code. This command uses a contiguous 32-bit request body length in bytes 10–13 and a 16-bit page number in bytes 14–15.

Header bytesSizeMeaning for cmd 75Notes
81UDP frame count / transport markerUse the normal value for TCP. Reserved for future UDP paging use.
91Large-upload flags/versionSet to 0 in Phase 1 unless a specific flag is defined.
10–134Request body lengthUnsigned big-endian byte length of the ENTIRE on-wire body: challenge(16) + command region + terminator(2) = 90 + page data length. The accounting mirrors command 70's 16-bit field at bytes 22–23 widened to 32-bit, but command 75's command region is 2 bytes larger because it carries an in-body page-number echo (see Request body). This is what the server reads/decrypts.
14–152Page numberUnsigned big-endian page number for the server-side stored object. Pages 0..65535 (Phase 1). The header value is authoritative for storage naming and must match the encrypted body echo.
22–232Legacy body-size fieldSet to 0xFFFF as a sentinel meaning "extended 32-bit length in 10–13 is authoritative." Command 75 must read bytes 10–13, never 22–23. (Sentinel chosen so any legacy reader cannot mis-frame a small body.)

Request body

The on-wire request body is framed exactly like every other encrypted QMail command:

challenge(16) + [ preamble(32) + command payload(40) + page data(N) ] + terminator(2)

The protocol layer prepends a 16-byte challenge and appends a 2-byte terminator. The challenge and the command region (preamble + command payload + page data) are encrypted with AES-128-CTR; the terminator is sent in the clear. The 32-bit header body length in bytes 10–13 counts the entire body: challenge + command region + terminator (see total below). The offsets in the tables below are relative to the start of the command region (i.e. immediately after the 16-byte challenge), matching command 70's layout.

Preamble (32 bytes, offsets 0–31)

Standard QMail identity preamble (QMAIL_PREAMBLE_SIZE = 32), identical to command 70. See QMail Overview — Universal preamble.

Two ways to count the preamble — same bytes

The QMail Overview describes a 48-byte preamble; this page uses 32. They describe the identical bytes from two viewpoints. The server (raidax, qmail_preamble_t) counts challenge(16) + identity block(32) = 48 as one unit. The client (rest_core) builds the 32-byte identity block as command data and the protocol layer prepends the 16-byte challenge separately. This page numbers offsets from the start of the command region (after the challenge), so the 32-byte identity block sits at offsets 0–31 here and at 16–47 in the server's 48-byte view. No bytes differ — only where offset 0 is placed.

Command payload (40 bytes, offsets 32–71)

OffsetSizeFieldDescription
32–4716File GUID16-byte email/file group identifier. Same GUID on every page and stripe of the email.
48–6316Locker CodeSender-controlled storage-payment locker key, null-padded to 16 bytes.
641File TypeSame file type mapping as command 70: body is 1, first attachment is 10, second is 11, and so on.
651Storage DurationStorage retention code.
66–672Page Number EchoUnsigned big-endian page number. MUST equal cleartext header bytes 14–15; the server rejects a mismatch (status 198). Anti-tamper / anti-misfile consistency check (not a cryptographic MAC under legacy AES-CTR).
68–714Page Data LengthLength of this page's data bytes, big-endian. Must be 1..262144 for Phase 1.
72…NPage DataThe page bytes for this server-side object and page number. N equals Page Data Length.

Command region length: 32 (preamble) + 40 (payload) + N (page data).

Total body length (bytes 10–13): 16 (challenge) + 32 (preamble) + 40 (payload) + N (page data) + 2 (terminator) = 90 + N. The 32-bit header body length in bytes 10–13 must equal this value exactly. The accounting mirrors command 70's 16-bit field (challenge + command region + terminator); command 75's command region is 2 bytes larger because of the in-body page-number echo.

Validation rules (server)

The Page Data Length field (command payload offsets 68–71) and the header body length (bytes 10–13) are two independent counts that the server MUST cross-check. They are redundant by design: bytes 10–13 bound the read off the socket before decryption; offsets 68–71 are read after decryption and confirm the page size inside the authenticated body.

  • Bound before allocate (anti-DoS): parse bytes 10–13 as UNSIGNED. Reject (status 16, ERROR_INVALID_PACKET_LENGTH) if it is 0 or exceeds the per-command maximum (90 + 262144 = 262234 for Phase 1). Never allocate or read more than this cap, regardless of the claimed value.
  • Self-consistency: after decrypt, require header_body_length (10-13) == 90 + PageDataLength (68-71) and 1 ≤ PageDataLength ≤ 262144. Reject with status 16 on mismatch.
  • Page-number echo: require the in-body page-number echo (offsets 66–67) to equal the cleartext header page number (bytes 14–15). Reject with status 198 (ERROR_INVALID_PARAMETER) on mismatch. This prevents header-only tampering from misfiling a page.
  • Terminator: the last two body bytes must be 3E 3E. Reject with status 16 otherwise.
  • Sentinel: bytes 22–23 are expected to be 0xFFFF for command 75; the server uses bytes 10–13 as the authoritative length and ignores 22–23 for framing.
  • Payment before durable write: validate the locker (read-only, local) before writing the page to disk (see Payment).

Paging rules

  • Page number is per server-side stored object. Two different RAIDAs may store objects with the same GUID, file type, and page number.
  • Page size is fixed at 256 KiB maximum for Phase 1.
  • The final page may be shorter than 256 KiB.
  • Uploads are sessionless. The client decides which pages are missing and retries them.
  • Storage must be idempotent by GUID + file type + page number + server. Re-uploading the same stored page (identical bytes) must not double-charge and returns success. Re-uploading the same GUID + file type + page number with different bytes is rejected (status 198) — stored page content is never silently overwritten.
  • Total page count, original file size, page size, and checksum/hash belong in the Tell manifest and private CBDF meta object, not in clear request header bytes.

On-disk naming contract: command 75 stores each page as the normal command-70 object name plus .p{PAGE}, where {PAGE} is the 5-digit zero-padded decimal page number. Examples: private meta page 0 is 00000000{GUID}.meta.p00000; body page 0 is 00000000{GUID}.qmail.p00000; attachment 1 page 3 is 00000000{GUID}.0.bin.p00003. Current clients normally page large attachments; the command itself is file-type neutral and may be used for larger meta/body objects in a future phase once the receiver advertises support.

Payment

Billing rounds stored data up to 1 MiB units. A sender may provide a locker with more funds than the upload requires; the extra value should remain unconsumed in the sender-controlled locker and can be reused by the sender later. This is escrow, not a server-held account balance.

The server should consume value only for unique pages that are durably stored. If an upload fails before all pages are uploaded, unconsumed locker value remains available to the sender and no refund command is required.

Response

No response body is required. The status byte in the response header indicates whether the page was stored or rejected.

Status codes

DecimalHexSymbolMeaning
2500xFASTATUS_SUCCESSPage stored, or duplicate page accepted idempotently.
160x10ERROR_INVALID_PACKET_LENGTHBody length, page length, or terminator mismatch.
1690xA9ERROR_PAYMENT_REQUIREDLocker is missing, invalid, or does not have enough value for the required billing unit.
1940xC2ERROR_FILESYSTEMServer could not create the storage path or durably write the page.
1980xC6ERROR_INVALID_PARAMETERInvalid file type, page number, duration, page size, or other field.
2000xC8ERROR_INVALID_ANPreamble AN does not match this RAIDA's stored AN for the sender coin.

Implementation notes

TCP first

Phase 1 large uploads use TCP. Keeping transport selection centralized makes it possible to enable UDP later for very small pages without changing the command's file/page semantics.

No hot-path AI dependency

DDoS detection may use offline or sidecar scoring, but command 75 must still have deterministic cheap validation before disk writes: bounded body length, valid encryption, valid preamble, funded locker escrow, fixed page size, and idempotent storage keys.

No session state

The server stores pages and returns statuses. The client owns retry, resume, final manifest construction, and tell publication.