Streaming-first
Serialize and deserialize in arbitrarily small chunks — you never need the whole message in memory. No length-prefixed envelope, so encoders emit data before the final size is even known.
Structured Objects For Anyone
… so optimized, feels amazing.
A compact, streamable binary format for structured data — tightly packed, schema-driven, and tiny enough for the smallest microcontroller yet fast enough for the cloud.
Each core library implements the same streamable wire format and shares a common suite of conformance test vectors — and some languages ship more than one build, each tuned for a different target.
Every byte and every design decision is tuned for moving structured data fast — across slow links and onto the tiniest devices — without complicating the code you write.
Serialize and deserialize in arbitrarily small chunks — you never need the whole message in memory. No length-prefixed envelope, so encoders emit data before the final size is even known.
Varint (LEB128) encoding shrinks small integers to a single byte, and zig-zag mapping keeps negative numbers compact. Low overhead is reserved for the field types you actually use most.
Type information lives in the stream itself: each field header packs (id << 3) | type. Decoders can read, descend, or skip any field — even ones they don't recognize.
Delimiter-based sequences create fresh ID namespaces for deeply nested, hierarchical structures — without ever pre-computing a size or buffering a sub-tree.
Write a definition once; the generator emits dead-simple, language-native objects with .serialize() / .deserialize() plus streaming variants.
A tiny footprint with no heavy runtime makes it equally at home on resource-constrained MCUs and high-throughput cloud services. Little-endian on the wire, identical on every architecture.
Born out of real IoT and embedded communication needs, where every existing format made an awkward trade-off.
"Because JSON is too fat,
and Protobuf is too Google."
Simple, predictable building blocks — varints, a 3-bit type tag, and delimiter-based scopes.
Each field starts with a single varint header. The low three bits select the wire type; everything above is the field ID (0 … 2,147,483,647).
# Field header = (id << 3) | type 0x0 unsigned int (varint) 0x1 signed int (zig-zag varint) 0x2 fixlen value (f32/f64/str/blob) 0x3 unsigned int array 0x4 signed int array 0x5 fixlen value array 0x6 sequence start (nested scope) 0x7 sequence end # Varints are little-endian, 7 bits each: 300 -> 0xAC 0x02 # Zig-zag keeps small negatives tiny: -1 -> 1 -2 -> 3
# Generated objects are dead simple person = Person() person.name = "Ada" person.id = 42 # One-shot convenience data = person.serialize() person2 = Person.deserialize(data) # ...or stream it in arbitrarily small chunks, # never holding the whole message in memory.
Power users can drive the raw encoder and decoder by hand for maximum control. Everyone else writes a schema and lets the generator produce language-native classes.
Every core library is held to the same bar — proven interoperable and kept fast — by two dedicated harnesses that run across all of them.
Our Crucible differential-fuzzing harness feeds identical bytes to every language at once and treats any disagreement as a bug. The result: implementations that stay byte-for-byte interoperable, round-trip clean, and hardened against malformed input.
The Arena suite measures every library head-to-head against Protocol Buffers — from raw encode/decode throughput on server CPUs to firmware footprint on bare-metal ARM and RISC-V. Fair, reproducible, and always watching for regressions.
Browse the core libraries, read the format specification, and start serializing structured data the lightweight way.