1. Envelope

July 26, 2026 · View on GitHub

The on-disk layout of an OBTF bundle is a fixed 8-byte header followed by a zstd-compressed MessagePack payload.

Byte layout

+---------+------------+----------------------------------+
| 4 bytes | 4 bytes    |  N bytes                         |
| "OBTF"  | uint32 LE  | zstd(msgpack(body))              |
+---------+------------+----------------------------------+
  magic     version      compressed body
  • Magic — the 4 ASCII bytes O, B, T, F (0x4F 0x42 0x54 0x46). Readers MUST verify these bytes before proceeding.
  • Version — a little-endian unsigned 32-bit integer holding the format's major version. This document specifies version 1.
  • Body — the payload: zstd.compress(msgpack.packb(body, use_bin_type=True)).

Readers MUST reject any bundle whose declared version is higher than the highest version they support. Readers SHOULD inspect the magic before attempting to decompress; a mismatched magic is a fatal error.

Compression

  • Codec: zstd.
  • Compression level: producer-chosen. Level 7 is RECOMMENDED for archive / share workflows; level 3 is RECOMMENDED for hot-write paths.
  • Readers MUST support decompression at any level supported by a standards-compliant zstd decoder. The compression level is not recorded in the envelope; zstd frames are self-describing.

Serialisation

  • The body MUST be encoded with MessagePack, using use_bin_type=True (i.e. the format distinguishes byte-strings from text-strings).
  • Map keys MUST be UTF-8 strings.
  • All timestamps in the msgpack body MUST be encoded as ISO 8601 strings with an explicit timezone offset (e.g. "2024-03-15T00:00:00+00:00"). Timestamps in Parquet blobs use a different representation — see 4. Blobs.
  • The body MUST be a single msgpack map at the root.

File extension and media type

  • Bundles SHOULD use the file extension .obtf.
  • The registered media type is application/vnd.quant-commons.obtf.
  • On systems that surface a "file type" (macOS, Windows), producers MAY register the extension with this media type. Readers MUST NOT rely on the extension for content sniffing — magic bytes are authoritative.

Atomicity

Writers MUST write bundles atomically: write to a sibling temp path (<path>.tmp) and then rename to the final path (os.replace on POSIX, MoveFileEx with MOVEFILE_REPLACE_EXISTING on Windows). Partially-written bundles MUST NOT appear at the final path.

Endianness

The version integer in the envelope header is little-endian. Integers inside the msgpack body follow msgpack's native encoding (big-endian on the wire); conforming msgpack libraries handle this transparently.

Minimum conforming body

The minimum conforming body MUST contain at least the following top-level keys:

KeyTypeNotes
format_versionuintMirrors the version in the envelope header.
algorithm_idstrProducer-defined stable identifier.
studiesmap[str, Study]MAY be an empty map.
blobsmap[str, bin]MAY be an empty map.

Readers MUST treat any additional top-level keys as opaque and preserve them on round-trip if writing back to disk. See 10. Extensions for the x-* extension convention.

Example: minimal bundle bytes

A conforming, essentially empty bundle looks like this (schematically):

Header  : 4F 42 54 46           # "OBTF"
Version : 01 00 00 00           # uint32 LE = 1
Body    : <zstd(msgpack({
            "format_version": 1,
            "algorithm_id":  "example-empty",
            "studies":       {},
            "blobs":         {}
          }))>

The uncompressed msgpack body is around 60 bytes; the zstd frame adds a small fixed header. A conforming reader MUST open this bundle successfully and produce an empty studies view.