Model or dataset
anthropics/buffa avatar
anthropics/buffa

buffa: a Rust protobuf library built around editions

Rust implementation of protobuf with editions support, JSON serialization, and zero-copy views

933 stars121 forksRustApache-2.0

At a glance

What is it?
buffa is a pure-Rust Protocol Buffers implementation from Anthropic that treats protobuf editions as its core abstraction, generates owned and zero-copy view types per message, and adds JSON, text format and runtime reflection. It is aimed at Rust teams whose schema uses editions or who need protobuf on no_std targets.
Who is it for?
Adopt buffa when your schema uses protobuf editions, or when you need a protobuf implementation that works on no_std targets with a Rust native API, and you are willing to track a pre-1.0 crate whose minor versions can break you. Skip it if your schema is plain proto2 or proto3 with no edition features and your existing Rust protobuf library already works, because the gain here is the editions model rather than raw speed.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • anthropics/buffa933 ★ · +8
Star history
Repository2026-10-222026-11-07
anthropics/buffa925933

8 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Editions are the abstraction, not an add-on

buffa is a pure-Rust Protocol Buffers implementation with first-class protobuf editions support. The README is direct about the gap it fills: the Rust ecosystem lacks an actively maintained, pure-Rust library that supports protobuf editions, and buffa was built ground-up with editions as the core abstraction.

What that means in practice is stated in the features list. Proto2 and proto3 are understood as feature presets within the editions model, giving one code path parameterized by resolved features. The difference is visible in the shape of the generated code. A library that treats editions as a layer on top of a proto3 engine has two engines and two sets of edge cases. A library where editions are the model has one, and proto2 and proto3 are configurations of it.

The compatibility claim is stated in the same paragraph: buffa passes the full protobuf conformance suite, binary, JSON and text, with zero expected failures. That is the claim to test, since conformance is a defined suite rather than a benchmark.

One disclosure worth noting up front, since it shapes how you should read the code: the README says buffa was written by Claude and friends. For a protobuf implementation, where the specification is precise and the test suite is mechanical, that is a plausible pairing, but it also means the review burden on you is higher, not lower.

Every message gets an owned type and a view

The type system is the second headline feature, and it is the one with the clearest performance argument.

Each message generates both an owned type, `MyMessage`, which is heap allocated, and a view type, `MyMessageView<'a>`, which is zero-copy from the wire. There is also `OwnedView<V>`, which wraps a view together with its backing `Bytes` buffer so it can be used across async boundaries without the borrow expiring.

That is a real design decision rather than a convenience. A protobuf payload often contains large byte fields and nested messages that a consumer only partially reads. With an owned type, decoding copies everything. With a view, the borrowed type points into the original buffer and only the parts actually touched are materialised. `OwnedView` exists because the obvious compromise, a view plus its buffer, is exactly what you need when the decoded message has to survive an await point, and it is easy to get wrong by hand.

Two smaller type decisions follow the same logic. `MessageField<T>` makes optional message fields deref to a default instance when unset, which removes the `Option<Box<T>>` unwrapping ceremony that shows up everywhere in hand written protobuf code. And `EnumValue<T>` gives type-safe open enums with proper Rust enum types that preserve unknown values, rather than exposing raw `i32` and silently losing data when a peer sends a value your build has never seen.

The unknown field preservation feature is in the same family. Round-trip fidelity for proxy and middleware use cases means a message that passes through your service does not lose fields it did not understand.

Linear-time serialization through size caching

The features list mentions linear-time serialization, with cached encoded sizes preventing the exponential blowup that affects libraries without a size-caching pass.

The mechanism is worth understanding because it explains why the design choices are not arbitrary. When a message is serialized, each nested message must report its own encoded length before it can be written, since the length prefix precedes the payload. A naive implementation asks each child for its size and each child asks its own children, and the cost of computing a size grows multiplicatively with nesting depth. Caching the size of each message when it is computed makes the total cost proportional to the size of the output instead.

That is a correctness-adjacent property as much as a performance one, because an exponential blowup turns a deeply nested message into something that hangs rather than something that is merely slow.

The other wire formats are covered too. The binary wire format supports all scalar types, nested messages, repeated and packed fields, maps, oneofs, groups and unknown fields. Proto3 JSON follows the canonical protobuf JSON mapping through optional serde integration, with well-known types handled the way the specification requires: Timestamp as RFC 3339, Duration as a string such as "1.5s", int64 and uint64 as quoted strings and bytes as base64. The text format, or textproto, is the human-readable debug format, covering `Any` expansion with its type URL, extension bracket syntax and group fields.

The text format and JSON paths are no_std compatible, which is unusual and useful for anyone shipping diagnostics into a constrained environment.

Reflection that avoids a re-encode

The `buffa-descriptor` crate, available under the `reflect` feature, provides a `DescriptorPool` and `DynamicMessage` for schema-driven encode, decode and JSON without generated code. It also covers extensions, custom-option access, `Any` pack and unpack, and symbol to file lookup for gRPC server reflection.

The part that makes this interesting is the second half of the paragraph. Generated types implement the same `ReflectMessage` trait directly, in what the README calls vtable mode, so `foo.reflect()` borrows in place. A CEL evaluator, a transcoding gateway or a generic interceptor can then treat typed and dynamic messages uniformly, without a re-encode round-trip.

That is the difference between reflection as an escape hatch and reflection as a first-class path. In many implementations, reading a message reflectively means decoding it again into a dynamic representation, which costs a copy and loses the connection to the typed values you already have. Here the typed value hands over a borrowed view of itself, so a middleware that wants to inspect or rewrite a message does not pay to rebuild it.

The README flags that there is a cost relative to generated code and points to the Reflection section of the documentation for the details, which is the right place to look before deciding that a hot path should use reflection.

no_std core, with std as an opt-in

The core runtime works without `std`, including JSON serialization through serde. Enabling the `std` feature adds `std::io` integration, `SystemTime` conversions and thread-local JSON parse options. Duration conversions use `core::time::Duration` and work without `std`.

The crate advertises itself as no_std compatible, and the categories in the workspace manifest include `encoding` and `no-std`. For a library that people often assume is tied to a full standard library, that is the difference between being usable on a microcontroller or a deeply embedded Linux target and not.

The one place the split shows is JSON parse options, and the README documents it as an unsupported feature rather than hiding it. Serde's `Deserialize` trait has no context parameter, so runtime options have to be passed through ambient state. In `std` builds, `with_json_parse_options` gives per-closure, per-thread scoping through a thread-local. In `no_std` builds, `set_global_json_parse_options` gives process-wide, set-once configuration through a global atomic. The two APIs are mutually exclusive and take the same options, and in `no_std` the options are fixed for the process and cannot vary between parse calls.

The other documented unsupported feature is proto2 optional-field getter methods. A `[default = X]` on an `optional` field does not generate a `fn field_name(&self) -> T` accessor that unwraps to the default; custom defaults are applied only to `required` fields through `impl Default`. Optional fields stay `Option<T>`, and you use pattern matching or `.unwrap_or(X)`.

Both of these are honest gaps with a stated reason, which is better than a silent difference in behaviour between editions.

Generating code with the buf remote plugin

The recommended path is `buf generate` with the published remote plugin, so no local plugin install is needed. The configuration file is a `buf.gen.yaml`:

yaml
version: v2
plugins:
  - remote: buf.build/anthropics/buffa
    out: src/gen
    opt:
      - file_per_package=true
      - json=true

and the generation step is:

sh
buf generate

That emits one `<dotted.package>.rs` file per proto package. The module tree itself is hand written, which is a small but deliberate choice, and the README shows the shape:

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

code

If you would rather have the module tree generated as well, you install `protoc-gen-buffa-packaging` locally and add it as a second plugin, dropping the `file_per_package=true` option:

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 ```

The repository also holds a `buffa-build` crate for use from a `build.rs`, and the README points at `examples/bsr-quickstart/` for a complete runnable project, plus a user guide covering local plugins, `buffa-build` and BSR generated SDKs.

The workspace manifest sets the Rust edition to 2021 and the minimum supported Rust version to 1.75, which is worth noting if your project is pinned to an older toolchain.

Pre-1.0 semver and a wide workspace

The API stability rules are spelled out in the README, and for a crate below 1.0 that is the part to read before you depend on it.

Buffa is pre-1.0 and follows the Rust community convention for 0.x crates: breaking changes increment the minor version, so 0.1.x to 0.2.0, and additive changes increment the patch version, so 0.1.0 to 0.1.1. The README advises pinning to a minor version, `buffa = "x.y"`, to avoid surprises, and notes that the full `x.y.z` that `cargo add buffa` writes is equivalent under caret semantics.

The generated code API, meaning the struct shapes, the `Message` trait, the `MessageView` trait, `EnumValue` and `MessageField`, is the primary stability surface. Internal helper modules marked `#[doc(hidden)]`, the `__private` and `__buffa_*` fields, may change at any time. That distinction is useful, because it tells you that the shape of generated code is the contract and the machinery behind it is not.

The workspace is wide, which reflects the scope. Members include `buffa` itself, `buffa-types`, `buffa-descriptor`, `buffa-proto-options`, `buffa-codegen`, `buffa-build`, `buffa-remote-derive`, `buffa-test`, `buffa-yaml`, `protoc-gen-buffa` and `protoc-gen-buffa-packaging`. There are also directories for `conformance/`, `fuzz/`, `benchmarks/`, `stress/`, `compat/`, `docs/` and `examples/`, and the examples include addressbook, bsr-quickstart, conflicts, custom-types, envelope, logging and buffa-smolstr.

The presence of `fuzz/`, `conformance/` and `compat/` directories is the strongest signal in that list, because those are the things that catch the class of bug a protobuf implementation is prone to.

On licence and maintenance, the workspace package fields set `license = "Apache-2.0"`, and the README and repository agree. Releases are frequent, with v0.9.0 on 2026-07-17, v0.9.1 on 2026-07-23 and v0.9.2 on 2026-09-03, and the last push was on 2026-10-18. Release notes are kept in CHANGELOG.md with a `.changes/` directory and a changie configuration, so the history is machine generated rather than hand written.

Editorial conclusion

Adopt buffa when your schema uses protobuf editions, or when you need a protobuf implementation that works on no_std targets with a Rust native API, and you are willing to track a pre-1.0 crate whose minor versions can break you. Skip it if your schema is plain proto2 or proto3 with no edition features and your existing Rust protobuf library already works, because the gain here is the editions model rather than raw speed. Verify first by generating your own schema with `buf generate` and the `buf.build/anthropics/buffa` remote plugin, then running the protobuf conformance suite against it, since that is the claim the project makes. The licence field on the repository says Apache-2.0, the last push was on 2026-10-18, and the current version is 0.9.2.

Frequently asked questions

What is buffa and why does it focus on protobuf editions?

buffa is a pure-Rust Protocol Buffers implementation with first-class protobuf editions support, written ground-up so that editions are the core abstraction and proto2 and proto3 become feature presets within that model. The README states it passes the full protobuf conformance suite, binary, JSON and text, with zero expected failures.

How do I generate Rust code with buffa?

The recommended path is buf with the published remote plugin. Create a `buf.gen.yaml` using `remote: buf.build/anthropics/buffa` with `out: src/gen` and the options `file_per_package=true` and `json=true`, then run `buf generate`. One `<dotted.package>.rs` file is emitted per proto package.

What is the difference between MyMessage and MyMessageView in buffa?

Each message generates both an owned, heap allocated type `MyMessage` and a borrowed view type `MyMessageView<'a>` that is zero-copy from the wire. `OwnedView<V>` wraps a view together with its backing `Bytes` buffer so it can be passed across async boundaries.

Does buffa support no_std?

Yes. The core runtime works without `std`, including JSON serialization through serde, and the text format and JSON paths are no_std compatible. Enabling `std` adds `std::io` integration, `SystemTime` conversions and thread-local JSON parse options. The minimum supported Rust version is 1.75.

What are the known limitations of buffa?

The README lists two unsupported features. Proto2 optional-field getter methods are not generated, so `[default = X]` on an optional field does not produce an accessor and optional fields stay `Option<T>`. And scoped `JsonParseOptions` in no_std builds are process-wide and set once, since serde's `Deserialize` trait has no context parameter.

How should I depend on buffa given it is pre-1.0?

The README says breaking changes increment the minor version and additive changes increment the patch version, following the Rust convention for 0.x crates, and advises pinning to a minor version such as `buffa = "x.y"`. The generated code API, including the `Message` and `MessageView` traits, `EnumValue` and `MessageField`, is the primary stability surface.

Official sources

  1. anthropics/buffa on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Community

Where developers are discussing it

Posts on Hacker News, dev.to and Lobsters that link to this repository, found by our scan of those communities. 1 in the last 30 days; newest first.

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/anthropics-buffa.svg)](https://hysenlabs.com/projects/anthropics-buffa)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/anthropics-buffa.svg)](https://hysenlabs.com/projects/anthropics-buffa)

Was this useful?