Favicon of Cap'n Proto

Cap'n Proto

Cap'n Proto is an MIT-licensed binary interchange format and capability-based RPC system from the author of Protocol Buffers version 2, designed so that the encoded bytes are directly usable in memory — there is no parse step between receiving a message and reading a field.

LicenseOpen source (permissive)· MIT
DeploymentSelf-hostedManaged
PricingFree
Operational complexityLow
WorkloadInteractiveStreaming
LanguageC++

Use it when

  • Latency and copy count dominate: the wire format is the in-memory format, so reading a field is a pointer dereference with no parse step, and large files can be mmapped.
  • Call chains are deep: promise pipelining collapses dependent RPC round trips into one, which is why Cloudflare Workers RPC is built on it.
  • Capability-based security fits the design: holding an object reference is the permission to call it.
  • The wire must never break: the 1.0 guarantee is no backwards-incompatible changes to the serialization or RPC protocol.

Think twice when

  • The value is in everything else speaking your format; Avro and protobuf have registries, Kafka serializers, and warehouse integrations, and Cap'n Proto has none of that.
  • You need reviewed implementations beyond C++; the other language bindings are third party and explicitly unreviewed, especially for the RPC half.
  • Advanced RPC levels matter; persistent capabilities and three-party handoff are specified but not implemented in the reference library.
  • Untrusted input is routine and nobody will configure traversal and depth limits.

How it runs

A schema compiler plus the C++ reference library; messages are arenas of 8-byte words with offset-based pointers, sent as-is or through an optional zero-byte packing scheme. RPC runs over ordinary sockets at protocol level 1. The 1.0 line is maintained as long-term support with backported fixes.

Details

Compare

How Cap'n Proto answers the questions Serialization Formats turns on.

Serialization Formats
How it works
EncodingThe arrangement a program would use in memory, written to the wire unchanged. Everything is built from 8-byte words: a struct is a data section of fixed-width fields followed by a pointer section, addressed by a struct pointer carrying a signed offset and the two section sizes, and the only four pointer kinds are struct, list, far (for crossing segment boundaries through a landing pad) and capability, which indexes a table rather than the message. There are no varints and no tags, so the bytes a sender holds are the bytes it sends. The cost is space, and the answer to that is an optional packing scheme that strips runs of zero bytes behind a one-byte tag per word, with a documented worst case of two bytes per 2 KiB, plus ordinary compression on top if wanted
Random accessThe reason this key exists, and the encoding page gives the mechanism rather than the slogan: the position of a field "depends only on its definition and the definitions of lower-numbered fields", so the compiler knows every offset and reading a field is an offset computation against a mapped buffer. No parse step, no allocation, no decode of the fields nobody asked for, a message can be mmapped and read in place. The part worth stating beside it is the consequence: trusting arbitrary bytes as a live object structure is a security problem, so the spec tells implementations to count the total data traversed and cap pointer depth to stop a small message expanding into an unbounded read
Self-describingShape yes, meaning no: a third answer beside Thrift's type tags and Avro's embedded schema. Because every struct is split into a data section and a pointer section, the specification notes the split "allows structs to be traversed (e.g., copied) without knowing their type", and canonicalising a message without its schema is possible too. So a generic reader can walk the object tree, copy it, and bound its size without knowing what any of it means. What it cannot do is tell a UInt32 from a Float32 in the data section, or name a single field: that requires the .capnp file, and there is no equivalent of Avro's schema-in-the-header
Schema evolutionExplicit and enumerated, with the rules written out as two lists rather than left to judgement. Fields carry ordinal numbers assigned in the order they were added, and position follows from those numbers, so appending a field cannot move an existing one. Safe: adding fields, enumerants and methods with higher numbers, adding method parameters at the end with defaults, renaming anything whose type ID is explicit, reordering declarations, and even making a non-generic type generic. Prohibited: changing any field, method or enumerant number, changing a field's type or default, changing a type ID, or renaming or moving a type that has no explicit ID. Ordinals-plus-offsets buys the same forward compatibility Thrift gets from field ids while keeping the fixed layout that makes random access work
Type systemThe richest in the capability, and the only one that models a remote object. Void, Bool, signed and unsigned integers at 8 through 64 bits, Float32 and Float64, Text as NUL-terminated UTF-8 and Data as raw bytes; List, enum, struct, union, and group for namespacing fields without a separate type. Then three the others lack: interface, whose methods make capabilities first-class values that can be passed inside messages; AnyPointer for a dynamically typed slot; and generics, so a Map(Key, Value) is expressible in the schema language rather than by convention. Constants and annotations round it out
RPCNot a bundled RPC framework but the thing the format was designed around, and the distinctive claim is latency rather than throughput: promise pipelining lets a client chain calls whose inputs are earlier calls' unreturned results, so a four-round-trip directory traversal collapses to one, and the documentation puts it as results returned "instantly, before the server even receives the initial request". Security comes from the same idea, an object reference is a capability, so receiving one is permission to call it and nobody else gains that permission. The protocol is defined in levels: 1 for pipelining and object references, 2 for persistent capabilities across connections, 3 for three-party handoff where two peers introduced by a third connect directly, 4 for reference equality. How much of that actually ships is the qualification the levels invite, and the C++ documentation supplies it: the reference implementation "is a Level 1 implementation", so persistent capabilities, three-party handoff and reference equality are specified rather than available
Spec versionsThe strongest guarantee here, and it comes from the 1.0 announcement rather than a spec page: "there is no plan to make any backwards-incompatible changes to the serialization format or RPC protocol". The 2.0 work that motivated declaring 1.0 at all is aimed at the C++ API, and the same post is explicit that "applications written in other languages are completely unaffected by all this", a major version that touches one implementation and leaves the wire alone, which is the reverse of what a version number usually threatens. 1.0 is maintained as a long-term support branch with backported fixes
Adoption and maturityBetter established than its niche reputation suggests, and the evidence is in what serves the project's own site: Cloudflare uses it extensively, and the 1.0 post claims Cap'n Proto encodes "millions (maybe billions) of messages and gigabits (maybe terabits) of data every single second of every day". About 13,200 stars and 1,067 forks, a repository going since March 2013, and steady releases the news page fails to mention, 1.1.0 in December 2024, then 1.2.0, 1.3.0, 1.4.0 and 1.5.0 in July 2026. The qualification is concentration rather than decline: one principal author, one reviewed implementation, and an adoption story that rests heavily on one company and the Sandstorm lineage it grew out of
Connections
Registry supportNone, and for this format it barely reads as a gap. No registry in this catalog's schema-registry leaf accepts it (Confluent, Karapace and AWS Glue take Avro, JSON Schema and Protobuf, Buf takes Protobuf alone, and Apicurio spends its nine artifact types elsewhere) so a Kafka platform standardising on a registry rules it out. The design points away from that world anyway: schemas are compiled into both ends, capability references are meaningless outside a live connection, and the format's centre of gravity is a process boundary rather than a durable topic
Generated codeRequired in practice, and narrow at the source: one official implementation, the C++ one by the format's author, covering serialization and RPC. Everything else is third-party and the project says so plainly, implementations "are maintained by respective authors and have not been reviewed". C#, Erlang, Go, Haskell, Node.js, OCaml, Python and Rust are listed with RPC as well as serialization; C, D, Java, Lua, Nim, Ruby and Scala with serialization only. That split matters more here than in any other row, because a format whose selling point is RPC is only as portable as the implementations that actually implement the RPC half

Share:

Alternatives to Cap'n Proto

Favicon

 

  
  
Favicon

 

  
  
Favicon