MCPcopy Index your code
hub / github.com/anthropics/buffa

github.com/anthropics/buffa @v0.8.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.8.1 ↗ · + Follow
5,406 symbols 20,335 edges 252 files 914 documented · 17%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

buffa

crates.io docs.rs CI MSRV deps.rs no_std License

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) and MyMessageView<'a> (zero-copy from the wire). OwnedView<V> wraps a view with its backing Bytes buffer for use across async boundaries.

  • MessageField<T>. Optional message fields deref to a default instance when unset -- no Option<Box<T>> unwrapping ceremony.

  • EnumValue<T>. Type-safe open enums with proper Rust enum types and preservation of unknown values, instead of raw i32.

  • 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 the reflect feature) provides DescriptorPool and DynamicMessage for schema-driven encode, decode, and JSON without generated code — plus extensions, custom-option access, Any pack/unpack, and symbol→file lookup for gRPC server reflection. Generated types implement the same ReflectMessage trait directly (vtable mode), so foo.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 for the cost relative to generated code.

  • no_std + alloc. The core runtime works without std, including JSON serialization via serde. Enabling std adds std::io integration, std::time conversions, 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 serde integration. 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 Any expansion ([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] on optional fields does not generate fn field_name(&self) -> T unwrap-to-default accessors. Custom defaults are applied only to required fields via impl Default. Optional fields are Option<T>; use pattern matching or .unwrap_or(X).
  • Scoped JsonParseOptions in no_std — serde's Deserialize trait has no context parameter, so runtime options must be passed through ambient state. In std builds, [with_json_parse_options] provides per-closure, per-thread scoping via a thread-local. In no_std builds, [set_global_json_parse_options] provides process-wide set-once configuration via a global atomic. The two APIs are mutually exclusive. The no_std global supports singular-enum accept-with-default but not repeated/map container filtering (which requires scoped strict-mode override).

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, unpacked repeated, and map values correctly. Packed blobs have no per-element tag to borrow, so the zero-copy UnknownFieldsView<'a> has no span to reference.

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 = "x.y"; the full x.y.z that cargo add buffa writes is equivalent under caret semantics) 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 <dotted.package>.rs file per proto package. Wire them into your crate with a small pub mod tree:

```rust,ignore // src/gen/mod.rs (hand-written) pub mod example { pub mod v1 { include!("example.v1.rs"); } }


If you'd rather have the module tree generated for you, install [`protoc-gen-buffa-packaging`](docs/guide.md#installing-the-protoc-plugins) locally and add it as a second plugin (drop the `file_per_package=true` opt):

```yaml
version: v2
plugins:
  - remote: buf.build/anthropics/buffa
    out: src/gen
    opt:
      - json=true
  - local: protoc-gen-buffa-packaging
    out: src/gen
    strategy: all

See examples/bsr-quickstart/ for a complete, runnable project, or the user guide for the full set of build setups (local plugins, buffa-build/build.rs, BSR-generated SDKs).

Using buffa-build in build.rs

Alternatively, use buffa-build for a build.rs-based workflow (requires protoc on PATH):

```rust,ignore // build.rs fn main() { buffa_build::Config::new() .files(&["proto/my_service.proto"]) .includes(&["proto/"]) .compile() .unwrap(); }


### Encoding and decoding

```rust,ignore
use buffa::Message;

// Encode
let msg = MyMessage { id: 42, name: "hello".into(), ..Default::default() };
let bytes = msg.encode_to_vec();

// Decode (owned)
let decoded = MyMessage::decode_from_slice(&bytes).unwrap();

// Decode (zero-copy view)
let view = MyMessageView::decode_view(&bytes).unwrap();
println!("name: {}", view.name); // &str, no allocation

// Decode (owned view — zero-copy + 'static, for async/RPC use)
let owned_view = MyMessageOwnedView::decode(bytes.into()).unwrap();
println!("name: {}", owned_view.name()); // still zero-copy, but 'static + Send

JSON serialization (with json feature)

rust,ignore let json = serde_json::to_string(&msg).unwrap(); let decoded: MyMessage = serde_json::from_str(&json).unwrap();

Documentation

  • User Guide — comprehensive guide to buffa's API, generated code shape, encoding/decoding, views, JSON, well-known types, and editions support.
  • Migrating from prost — step-by-step migration guide with before/after code examples.
  • Migrating from protobuf — migration guide covering both stepancheg v3 and Google official v4.

Workspace layout

Crate Purpose
buffa Core runtime: Message trait, wire format codec, no_std support
buffa-types Well-known types: Timestamp, Duration, Any, Struct, wrappers, etc.
buffa-descriptor Protobuf descriptor types (FileDescriptorProto, DescriptorProto, ...)
buffa-codegen Code generation from protobuf descriptors
buffa-build build.rs helper for invoking codegen via protoc
protoc-gen-buffa protoc plugin binary; also published as buf.build/anthropics/buffa
protoc-gen-buffa-packaging protoc plugin that emits a mod.rs module tree (local-only)

Performance

Throughput comparison across five representative message shapes, measured at buffa v0.8.0 on a quiesced bare-metal Intel Xeon Platinum 8488C (turbo disabled, performance governor, one core per implementation, median of five passes; full methodology in benchmarks/charts/README.md). Higher is better.

Binary decode

Binary decode — ApiResponse Binary decode — LogRecord Binary decode — AnalyticsEvent Binary decode — GoogleMessage1 Binary decode — MediaFrame

Raw data (MiB/s)

Message buffa buffa (view) buffa (lazy) prost prost (bytes) protobuf-v4 Go
ApiResponse 575 872 (+52%) 912 (+59%) 550 (−4%) 546 (−5%) 430 (−25%) 175 (−70%)
LogRecord 572 1,336 (+134%) 1,716 (+200%) 481 (−16%) 477 (−17%) 555 (−3%) 161 (−72%)
AnalyticsEvent 123 225 (+83%) 11,873 (+9538%) 148 (+20%) 129 (+5%) 222 (+80%) 57 (−54%)
GoogleMessage1 601 786 (+31%) 1,452 (+142%) 698 (+16%) 669 (+11%) 373 (−38%) 263 (−56%)
MediaFrame 10,619 41,441 (+290%) 40,678 (+283%) 6,002 (−43%) 18,432 (+74%) 11,005 (+4%) 1,890 (−82%)

Binary encode

Binary encode — ApiResponse Binary encode — LogRecord Binary encode — AnalyticsEvent Binary encode — GoogleMessage1 Binary encode — MediaFrame

Raw data (MiB/s)

Message buffa buffa (view) buffa (lazy) prost prost (bytes) protobuf-v4 Go
ApiResponse 1,972 1,955 (−1%) 1,959 (−1%) 1,964 (−0%) 639 (−68%) 384 (−81%)
LogRecord 3,053 3,509 (+15%) 3,587 (+17%) 2,758 (−10%) 1,067 (−65%) 186 (−94%)
AnalyticsEvent 404 426 (+6%) 12,960 (+3109%) 238 (−41%) 307 (−24%) 105 (−74%)
GoogleMessage1 2,122 2,123 (+0%) 2,965 (+40%) 1,816 (−14%) 522 (−75%) 232 (−89%)
MediaFrame 25,727 27,325 (+6%) 27,441 (+7%) 25,402 (−1%) 6,671 (−74%) 2,423 (−91%)

Build + binary encode

The build + encode measure starts from raw field values rather than a pre-built message struct, so it counts struct construction. The buffa (view) path constructs a borrowed view directly over the input slices and never allocates an owned message at all, which is why it is consistently faster than building owned structs and then encoding them.

Build + binary encode — ApiResponse Build + binary encode — LogRecord Build + binary encode — AnalyticsEvent Build + binary encode — GoogleMessage1 Build + binary encode — MediaFrame

Raw data (MiB/s)

Message buffa buffa (view)
ApiResponse 639 1,224 (+91%)
LogRecord 315 2,447 (+678%)
AnalyticsEvent 267 802 (+200%)
GoogleMessage1 673 900 (+34%)
MediaFrame 14,432 33,481 (+132%)

JSON encode

JSON encode — ApiResponse JSON encode — LogRecord JSON encode — AnalyticsEvent JSON encode — GoogleMessage1 JSON encode — MediaFrame

Raw data (MiB/s)

Message buffa prost Go
ApiResponse 521 589 (+13%) 70 (−87%)
LogRecord 697 882 (+27%) 85 (−88%)
AnalyticsEvent 5

Extension points exported contracts — how you extend this code

DefaultInstance (Interface)
Provides access to a lazily-initialized, immutable default instance of a type. Types that implement this trait can be u [12 …
buffa/src/message_field.rs
ReflectMap (Interface)
A reflective view over a map field's entries. `DynamicMessage` (bridge mode) implements this for [`MapValue`]. Vtable m [5 …
buffa-descriptor/src/reflect/value.rs
ServiceA (Interface)
(no doc) [15 implementers]
docs/investigations/e0477-owned-view-send/src/lib.rs
Enumeration (Interface)
Trait implemented by all generated protobuf enum types. [33 implementers]
buffa/src/enumeration.rs
ReflectElement (Interface)
(no doc) [41 implementers]
buffa-descriptor/src/reflect/containers.rs
ServiceB (Interface)
(no doc) [2 implementers]
docs/investigations/e0477-owned-view-send/src/lib.rs
MessageView (Interface)
Trait for zero-copy borrowed message views. View types borrow from the input buffer and provide read-only access to mes [59 …
buffa/src/view.rs
ReflectMessage (Interface)
(no doc) [35 implementers]
buffa-descriptor/src/reflect/message.rs

Core symbols most depended-on inside this repo

unwrap
called by 1276
buffa/src/message_field.rs
expect
called by 777
buffa/src/message_field.rs
push
called by 671
buffa-test/src/lib.rs
check_wire_type
called by 429
buffa/src/encoding.rs
reborrow
called by 283
buffa/src/view.rs
iter
called by 262
buffa-types/src/value_ext.rs
encode_to_vec
called by 258
buffa-descriptor/src/reflect/dynamic.rs
from_str
called by 235
buffa-yaml/src/decode.rs

Shape

Function 3,297
Method 1,550
Class 425
Enum 96
Interface 37
Struct 1

Languages

Rust99%
Python1%
Go1%

Modules by API surface

buffa-descriptor/src/generated/google.protobuf.descriptor.__view.rs202 symbols
buffa/src/view.rs187 symbols
buffa-descriptor/src/generated/google.protobuf.descriptor.rs179 symbols
buffa/src/json_helpers/tests.rs172 symbols
buffa/src/types.rs168 symbols
buffa-codegen/src/context.rs134 symbols
buffa/src/message.rs118 symbols
buffa/src/extension.rs106 symbols
buffa/src/extension_registry.rs93 symbols
buffa-build/src/lib.rs91 symbols
buffa-codegen/src/impl_message.rs80 symbols
buffa-codegen/src/comments.rs76 symbols

For agents

$ claude mcp add buffa \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact