CBDF Specification: Document Structure

Version 1.1 (Phase II)

Document: 01-Document-Structure • Date: 2026-07-13

1. Overview

A CBDF document is a binary file divided into five major sections, separated by FS (0x1C) markers. Core sections are identified by position, not by a per-section file-ID byte.

[Meta] FS [Styles] FS [Text] FS [Resources] FS [Logic]
[ optional: FS [SectionID:1][Len:4 LE][payload] ... ]

Text precedes Resources to enable abortable downloads: clients can stop after text without receiving image data. All multi-byte integers are little-endian.

CBDF Document Structure Diagram

Click diagram to open full size in new tab

2. Section Overview

#SectionLength PrefixCompressed?Required?Description
1MetaNo (KV pairs)NeverYesDocument envelope
2Styles4 bytes LEYes*Yes**LayoutID + style tables
3Text4 bytes LEYes*Yes**UTF-8 + control codes
4Resources4 bytes LENeverSlot yes; payload may be emptyImages, fonts, etc.
5Logic4 bytes LESlot yes; length 0 in Phase IIExecutable (Phase III)

Notes

* Styles and Text compress together when Meta key 31 is non-zero.

** May be zero-length but FS markers and length fields are present in Phase II (except meta-only documents). Canonical encoders always emit Resources and Logic tails.

3. Phase I Structure (backward compatible)

[Pair Count: 2 bytes LE] [Meta KV pairs]
0x1C
0x1C
0x02 [Plain text body] EOF

No Meta key 30 (version 0). Empty styles implied by consecutive FS. No Resources/Logic. Body starts with STX and continues until EOF. ETX is not required. Implemented by qmail_cbdf.c.

Do not add a leading magic byte before the pair count — that breaks deployed Phase I files.

4. Phase II Structure

4A. Uncompressed (version ≥ 1)

[Pair Count: 2 LE] [Meta KV pairs including key 30 = 1]
0x1C [StylesLen:4 LE] [Styles bytes]
0x1C [TextLen:4 LE] [STX ... markup ... ETX]
0x1C [ResourcesLen:4 LE] [Resource blobs]
0x1C [LogicLen:4 LE]          ; must be 0 in Phase II

The 4-byte length appears after FS and is not included in the length value. Length counts only section payload bytes.

FS [Length=N: 4 bytes LE] [N bytes of section data]

Empty tails (canonical encode): always emit Resources and Logic as FS + 0x00000000 when empty (10 bytes total). Fixed state machine for parsers.

Lenient decode: parsers MAY accept EOF immediately after a valid Text section and treat Resources/Logic as empty. New encoders MUST NOT rely on that shortcut.

Non-zero Logic in a version-1 document: hard-fail until Phase III defines Logic.

4A2. Compressed (key 31 ∈ {1..4})

[Meta]
0x1C [CompLen:4 LE] [DecompLen:4 LE] [compressed data]
0x1C [ResourcesLen:4 LE] [Resources]
0x1C [LogicLen:4 LE = 0]

After decompression, the blob MUST contain:

[StylesLen:4 LE] [Styles]
0x1C
[TextLen:4 LE] [STX ... ETX]

The FS between Styles and Text is inside the compressed blob. Meta and Resources are never CBDF-compressed.

4B. Meta-only (key 33 = 1)

[Pair Count: 2 LE] [Meta KV pairs including Version and EOF flag]
; no FS markers follow

Used for ultra-compact notifications and SMS-class messages.

5. QMail Transport Packaging

Do not confuse CBDF logical sections with Tell manifest file types:

Tell file_typeContent
0Private Meta KLV (may end with no trailing FS)
1Body/content: FS [Styles] FS [Text] FS [Resources] FS [Logic]
10+Attachments

Phase I body objects begin FS FS STX. Phase II body objects begin FS [styles_len]…. Offsets such as Meta key 40 (Text Offset) are relative to byte 0 of the containing object.

Phase II keeps Resources embedded in file_type=1. Splitting large resources into separate objects (e.g. when total email exceeds ~14,000 bytes ≈ 1,400 B UDP × 10 RAIDA stripes) is a Phase III transport policy.

6. Compression

Key 31CodecPhase II rule
0NoneRequired
1DEFLATE/zlibMandatory to implement; default encoder output
2LZ4Optional decode; emit only if peer capability known
3ZstandardOptional decode; emit only if peer capability known
4BrotliOptional decode; emit only if peer capability known
5Semantic encodingExperimental — Phase III; no Phase II implementation requirement
6–255ReservedReject if required

Encoders SHOULD skip compression when uncompressed Styles+Text total is less than 256 bytes.

QMail Phase II has no codec negotiation channel: emit zlib or none unless peer support is known out of band.

7. Extension Sections

After Logic, a document MAY contain additional sections:

FS [SectionID:1] [Len:4 LE] [payload]

IDs are registry-assigned. Phase II parsers MUST skip unknown IDs by length. Section ID 1 is reserved for a future Phase III Parse Index (optional byte offsets into Text for parallel workers). IDs for Listeners/Relationships land here without a version bump.

8. Byte Order and File Extensions

All multi-byte integers in CBDF are little-endian. (QMail Tell wire fields use big-endian; convert at the boundary.)

ExtensionMeaning
.qmailQMail email document
.qwebQWeb page document
.cbdfGeneric CBDF document

9. Parallel Parsing

CBDF is conducive to coarse-grained parallel work:

  • One sequential scan builds a section table from FS + lengths.
  • Style sub-tables are packed (count × fixed stride) and indexable after headers are known.
  • Resources are length-prefixed blobs; image decode fans out on a thread pool with width/height placeholders from Image definitions.
  • LayoutID implies a pane list; paint can parallelize per pane after a serial Text walk.

Serial choke points (accepted): (1) single zlib blob for Styles+Text; (2) Text control stream (style stack and self-delimiting payloads). Recommended pipeline: Meta → decompress → parallel resources + style tables → serial Text → display list → parallel pane paint.

Optional Phase III Parse Index (extension section ID 1) can add seekable Text slices without changing Phase II wire for small documents.

10. Migration

Document → ClientPhase I clientPhase II client
v0FullFull (no key 30)
v1Meta OK; body may fail old FS FS STX checkFull
Meta-onlySubject may displayFull

Product rule: ship a tolerant Phase I decoder before any Phase II encoder. If key 30 ≥ 1 or key 33 = 1, do not demand FS FS STX; show Preview Text / AI Summary / Subject labeled as newer-client content. Phase II encoders SHOULD emit key 36 during the transition era and MUST keep Phase I required keys unchanged.