anthropics/buffa
Rust
Captured source
source ↗anthropics/buffa
Description: Rust implementation of protobuf with editions support, JSON serialization, and zero-copy views
Language: Rust
License: Apache-2.0
Stars: 766
Forks: 61
Open issues: 14
Created: 2026-03-06T16:47:32Z
Pushed: 2026-06-11T00:09:29Z
Default branch: main
Fork: no
Archived: no
README:
buffa
 
A pure-Rust Protocol Buffers implementation with first-class protobuf editions support. Written by Claude and friends ❣️
Why buffa?
The Rust ecosystem lacks an actively maintained, pure-Rust library that supports protobuf editions. Buffa fills that gap with a ground-up design that treats editions as the core abstraction. It passes the full protobuf conformance suite — binary, JSON, and text — with zero expected failures.
Features
- Editions-first. Proto2 and proto3 are understood as feature presets within the editions model. One code path, parameterized by resolved features.
- Two-tier owned/borrowed types. Each message generates both
MyMessage(owned, heap-allocated) andMyMessageView(zero-copy from the wire).OwnedViewwraps a view with its backingBytesbuffer for use across async boundaries.
- `MessageField`. Optional message fields deref to a default instance when unset -- no
Option>unwrapping ceremony.
- `EnumValue`. Type-safe open enums with proper Rust
enumtypes and preservation of unknown values, instead of rawi32.
- Linear-time serialization. Cached encoded sizes prevent the exponential blowup that affects libraries without a size-caching pass.
- Unknown field preservation. Round-trip fidelity for proxy and middleware use cases.
- Runtime reflection.
buffa-descriptor(under thereflectfeature) providesDescriptorPoolandDynamicMessagefor schema-driven encode, decode, and JSON without generated code — plus extensions, custom-option access,Anypack/unpack, and symbol→file lookup for gRPC server reflection. Generated types implement the sameReflectMessagetrait directly (vtable mode), sofoo.reflect()borrows in place and a CEL evaluator, transcoding gateway, or generic interceptor treats typed and dynamic messages uniformly — without a re-encode round-trip. See [Reflection](#reflection) for the cost relative to generated code.
- `no_std` + `alloc`. The core runtime works without
std, including JSON serialization via serde. Enablingstdaddsstd::iointegration,std::timeconversions, and thread-local JSON parse options.
Wire formats
buffa supports binary, JSON, and text protobuf encodings:
- Binary wire format -- full support for all scalar types, nested messages, repeated/packed fields, maps, oneofs, groups, and unknown fields.
- Proto3 JSON -- canonical protobuf JSON mapping via optional
serdeintegration. Includes well-known type serialization (Timestamp as RFC 3339, Duration as"1.5s", int64/uint64 as quoted strings, bytes as base64, etc.).
- Text format (`textproto`) -- the human-readable debug format. Covers
Anyexpansion ([type.googleapis.com/...] { ... }), extension bracket syntax ([pkg.ext] { ... }), and group/DELIMITED fields.no_std-compatible.
Unsupported features
These are intentionally out of scope:
- Proto2 optional-field getter methods —
[default = X]onoptionalfields does not generatefn field_name(&self) -> Tunwrap-to-default accessors. Custom defaults are applied only torequiredfields viaimpl Default. Optional fields areOption; use pattern matching or.unwrap_or(X). - Scoped `JsonParseOptions` in `no_std` — serde's
Deserializetrait has no context parameter, so runtime options must be passed through ambient state. Instdbuilds, [with_json_parse_options] provides per-closure, per-thread scoping via a thread-local. Inno_stdbuilds, [set_global_json_parse_options] provides process-wide set-once configuration via a global atomic. The two APIs are mutually exclusive. Theno_stdglobal supports singular-enum accept-with-default but not repeated/map container filtering (which requires scoped strict-mode override).
[with_json_parse_options]: https://docs.rs/buffa/latest/buffa/json/fn.with_json_parse_options.html [set_global_json_parse_options]: https://docs.rs/buffa/latest/buffa/json/fn.set_global_json_parse_options.html
Known limitations
These are gaps we intend to address in future releases:
- Closed-enum unknown values in packed-repeated view decode are silently dropped (not routed to unknown fields). The owned decoder handles this correctly; the view decoder handles singular, optional, oneof, and unpacked repeated correctly. Packed blobs have no per-element tag to borrow, so the zero-copy
UnknownFieldsViewhas no span to reference. - Closed-enum unknown values in map values are silently dropped (not routed to unknown fields). The proto spec requires the entire map entry (key + value) to go to unknown fields, which requires re-encoding. This affects proto2 schemas with
mapwhere an evolved sender adds new enum values.
Semver and API stability
Buffa is pre-1.0. We follow the Rust community convention for 0.x crates: breaking changes increment the minor version (0.1.x → 0.2.0), additive changes increment the patch version (0.1.0 → 0.1.1). Pin to a minor version (buffa = "0.7") to avoid surprises.
The generated code API (struct shapes, Message trait, MessageView trait, EnumValue, MessageField) is considered the primary stability surface. Internal helper modules marked #[doc(hidden)] (__private, __buffa_* fields) may change at any time.
Quick start
Using buf generate (recommended)
Install buf, then create a buf.gen.yaml that uses the published `buf.build/anthropics/buffa` remote plugin — no local plugin install required:
version: v2 plugins: - remote: buf.build/anthropics/buffa out: src/gen opt: - file_per_package=true - json=true
buf generate
This emits one .rs file per proto…
Excerpt shown — open the source for the full document.
Notability
notability 5.0/10New Anthropic repo, moderate stars