Skip to content

Add anthropics/buffa plugin v0.3.0#2333

Open
iainmcgin wants to merge 1 commit intobufbuild:mainfrom
iainmcgin:add-buffa-plugin
Open

Add anthropics/buffa plugin v0.3.0#2333
iainmcgin wants to merge 1 commit intobufbuild:mainfrom
iainmcgin:add-buffa-plugin

Conversation

@iainmcgin
Copy link
Copy Markdown

@iainmcgin iainmcgin commented Mar 17, 2026

Adds the protoc-gen-buffa plugin for Rust protobuf code generation, under the new top-level anthropics org as requested in earlier review.

About buffa

buffa is a zero-copy Rust Protobuf implementation with:

  • Editions support (2023/2024) and full proto2/proto3 coverage
  • Zero-copy view types - borrowed &str/&[u8] field access without owned allocation
  • no_std + alloc compatible
  • Proto3 JSON mapping with serde integration
  • Conformance: passes the protobuf conformance suite (5539 binary+JSON tests, 0 expected failures)

Crates are on crates.io: buffa, buffa-types, protoc-gen-buffa.

Output convention

The plugin emits one .rs file per proto file with a flat naming scheme: foo/v1/bar.proto -> foo.v1.bar.rs. No subdirectories, no mod.rs - module tree assembly is a separate packaging plugin (protoc-gen-buffa-packaging, submitted separately).

Open questions for review

  1. opts: I set json=true and views=true. If BSR has conventions for which opts to expose vs bake in, please advise.
  2. File naming: The per-file output naming (pkg.subpkg.file.rs) is flat but per-proto-file, not per-package like prost. If BSR's lib.rs synthesis expects per-package naming instead, we can add a plugin option to switch modes.

Process note

CONTRIBUTING.md asks for an issue first - happy to open one if you would prefer to discuss before reviewing the PR. Filed this as a draft in the meantime.

Related: #2334 (anthropics/connect-rust) declares this plugin as a dep, so this one would need to land first.

@bufdev
Copy link
Copy Markdown
Member

bufdev commented Mar 19, 2026

I think we might want to make a top-level anthropics org for this, instead of under community - also re: connect-rust.

@iainmcgin
Copy link
Copy Markdown
Author

I think we might want to make a top-level anthropics org for this, instead of under community - also re: connect-rust.

Sure thing I'll redraft as part of updating to the new versions I'll likely release next week.

@iainmcgin iainmcgin changed the title Add community/anthropics-buffa plugin v0.2.0 Add anthropics/buffa plugin v0.2.0 Apr 2, 2026
@iainmcgin iainmcgin changed the title Add anthropics/buffa plugin v0.2.0 Add anthropics/buffa plugin v0.3.0 Apr 2, 2026
@@ -0,0 +1,4 @@
source:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's supposed to be a crates section here? See the prost plugin as an ezample

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to crate ref instead of github ref 👍

buffa is a zero-copy Rust Protobuf implementation with editions support
(2023/2024), no_std compatibility, and view types for borrowed field
access. Passes the protobuf conformance suite (5539 binary+JSON tests).

The plugin emits one .rs file per proto file using a flat naming
convention (`foo/v1/bar.proto` -> `foo.v1.bar.rs`). Module tree
assembly is a separate `protoc-gen-buffa-packaging` plugin in the same
repo, registered separately.

Crates: https://crates.io/crates/protoc-gen-buffa
Repo: https://github.com/anthropics/buffa
@iainmcgin iainmcgin marked this pull request as ready for review April 3, 2026 15:54
Copy link
Copy Markdown
Member

@pkwarren pkwarren left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ensure reproducible builds, we check in a checksum file of generated code using the plugin. This verifies e2e code generation is functional and also ensures that the codegen doesn't change across builds.

https://github.com/bufbuild/plugins/blob/main/CONTRIBUTING.md#creating-a-new-plugin

To create the files, run make test PLUGINS=anthropics/buffa:latest and check in the plugin.sum files created under tests/testdata/buf.build/anthropics/.

@@ -0,0 +1,15 @@
# syntax=docker/dockerfile:1.19
FROM rust:1.91.1-alpine3.22 AS builder
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update this branch to latest main then we can update this image to the latest base image:

Suggested change
FROM rust:1.91.1-alpine3.22 AS builder
FROM rust:1.95.0-alpine3.23@sha256:606fd313a0f49743ee2a7bd49a0914bab7deedb12791f3a846a34a4711db7ed2 AS builder

RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked --mount=type=cache,target=/root/target \
cargo install protoc-gen-buffa --version 0.3.0 --locked --root /app

FROM gcr.io/distroless/static-debian12:latest@sha256:87bce11be0af225e4ca761c40babb06d6d559f5767fbf7dc3c47f0f1a466b92c AS base
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
FROM gcr.io/distroless/static-debian12:latest@sha256:87bce11be0af225e4ca761c40babb06d6d559f5767fbf7dc3c47f0f1a466b92c AS base
FROM gcr.io/distroless/static-debian13:latest@sha256:47b2d72ff90843eb8a768b5c2f89b40741843b639d065b9b937b07cd59b479c6 AS base

Comment thread plugins/anthropics/buffa/v0.3.0/buf.plugin.yaml
@pkwarren pkwarren requested a review from stefanvanburen April 20, 2026 16:43
@stefanvanburen
Copy link
Copy Markdown
Member

  1. opts: I set json=true and views=true. If BSR has conventions for which opts to expose vs bake in, please advise.

Seems reasonable to me to enable both — since generated SDKs aren't configurable, I think including all of the features makes sense to me (users who want more control can fall back to local generation).

2. File naming: The per-file output naming (pkg.subpkg.file.rs) is flat but per-proto-file, not per-package like prost. If BSR's lib.rs synthesis expects per-package naming instead, we can add a plugin option to switch modes.

What would be most helpful here is actually if both buffa and connect-rust included the lib.rs generation logic themselves (enabled with a plugin option; generate_librs=true strawman name; disabled by default) so that the BSR doesn't need any sort of special-casing for generating it on our end — is that possible to do?

@iainmcgin
Copy link
Copy Markdown
Author

@stefanvanburen buffa has a plugin for local usage, protoc-gen-buffa-packaging, which produces a consolidated mod.rs separately from the core plugin. This was the structure that @bufdev wanted me to use because that plugin requires strategy: all to get visibility across all files in one pass. I'm not sure if we can generate lib.rs without strategy: all for similar reasons, but open to suggestions.

@iainmcgin
Copy link
Copy Markdown
Author

[claude code] We'll add a file_per_package=true opt to protoc-gen-buffa that emits one <dotted.package>.rs per proto package (concatenating what currently goes into the per-proto-file outputs). That matches the convention the existing cargo-SDK lib.rs synthesis already handles for prost, so no special-casing should be needed on the BSR side. It's also correct under strategy: directory for any PACKAGE_DIRECTORY_MATCH-clean module, so it avoids the strategy: all dependency @bufdev flagged earlier.

connect-rust will get the same opt and also emit <dotted.package>.rs — since the buffa and connect-rust SDKs are separate crates there's no collision, and the service stubs land at the proto package path (<connect_sdk>::pet::v1::PetServiceClient) rather than under an extra submodule the way tonic's <pkg>.tonic.rs ends up.

Targeting v0.4.1 for both. I'll update these PRs once that's released.

Separately: as far as I can tell registry.cargo only exposes rust_version and deps, so the filename→module-tree synthesis is an implicit contract that plugins have to reverse-engineer from prost's output. Would it be worth either documenting that convention in CONTRIBUTING.md, or adding something like registry.cargo.lib_rs: plugin so a plugin can opt into emitting its own lib.rs and have BSR use it verbatim? Happy to open a separate issue if that's a better place for it.

@stefanvanburen
Copy link
Copy Markdown
Member

We'll add a file_per_package=true opt to protoc-gen-buffa that emits one <dotted.package>.rs per proto package (concatenating what currently goes into the per-proto-file outputs).

@iainmcgin sounds good, thanks for your persistence with this, it's appreciated!

Separately: as far as I can tell registry.cargo only exposes rust_version and deps, so the filename→module-tree synthesis is an implicit contract that plugins have to reverse-engineer from prost's output. Would it be worth either documenting that convention in CONTRIBUTING.md, or adding something like registry.cargo.lib_rs: plugin so a plugin can opt into emitting its own lib.rs and have BSR use it verbatim? Happy to open a separate issue if that's a better place for it.

You're basically on-the-nose here, unfortunately: with basically every other package ecosystem, it's relatively straightforward to package up plugin output; Rust is a different story, and the BSR has some workarounds here to handle prost / tonic as a special snowflake, given that they were the only real game in town when we built out Rust generated SDKs. Now that there's more to the ecosystem, we'll hopefully be able to abstract that away a little more.

iainmcgin added a commit to anthropics/buffa that referenced this pull request Apr 29, 2026
…73)

Adds a `file_per_package` option to `CodeGenConfig` (and the
`protoc-gen-buffa` CLI) that emits one `<dotted.package>.rs` per proto
package instead of the default per-proto-file split. The single file
inlines what the `<pkg>.mod.rs` stitcher would otherwise `include!`,
producing the identical `__buffa::` module structure.

## Why

BSR's cargo-SDK pipeline assembles `lib.rs` server-side by splitting
each output filename on `.` and building a nested `pub mod` tree — the
same convention prost's per-package output follows. Our default per-file
output (`pet.v1.pet.rs`, `pet.v1.pet.__view.rs`, `pet.v1.mod.rs`, ...)
would synthesize to garbage under that algorithm. With
`file_per_package=true` set in the BSR plugin manifest's
`registry.opts`, our output slots into the existing synthesis with no
special-casing on Buf's side. See bufbuild/plugins#2333.

This mode is correct under `strategy: directory` for
`PACKAGE_DIRECTORY_MATCH`-clean modules (one directory == one package,
so each invocation sees a complete package), so it does not introduce a
`strategy: all` requirement.

## Implementation

- `PackageSections` intermediate holds per-section `Vec<TokenStream>`s;
`generate_package_mod` now consumes it uniformly. The per-file path
builds sections from `include!` token streams; the per-package path
inlines the content directly. Module structure is identical between
modes.
- `package_to_filename()` helper for `<pkg>.rs` naming.
- `GeneratedFileKind::PackageMod` is reused for the single-file output.

## Deferred (Low review findings)

- `OutputLayout` enum instead of `bool` — only one alternate layout
exists; revisit if more emerge.
- Unnamed-package output is `__buffa.rs`, which under BSR's synthesis
would double-nest. Buf's `PACKAGE_DEFINED` lint (MINIMAL group) makes
this a near-zero case for SDK gen, and it mirrors the existing per-file
path's behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants