Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Buffy Book

Buffy Logo

Buffy is a Protocol Buffers build and publishing tool. From a single set of .proto files, Buffy generates idiomatic libraries for multiple languages, packages each one according to its ecosystem’s conventions, and publishes them to their respective registries (e.g. crates.io, Maven Central, npm) or to Git. You can contribute to this book on GitHub.

Sections

Getting Started

Install Buffy and set up your first project that generates protocol buffer libraries for multiple languages.

Buffy Reference

The reference covers the details of every area of Buffy: the manifest format, profile configuration per language, environment variables, and the command-line interface.

Frequently Asked Questions

Appendices:

Getting Started

To get started with Buffy, install it and set up your first project that generates protocol buffer libraries for multiple languages.

Installation

Buffy is distributed as a single binary. Install it with:

curl -sSL https://pkgs.julian-siebert.de/buffy/install.sh | sh

The installer places the buffy binary in ~/.local/bin/buffy and prints instructions if that directory is not already on your PATH.

Verify the installation:

buffy --version

External tools

Buffy generates code by invoking language-specific tools. You only need the tools for the languages you actually target — Buffy reports a clear diagnostic if a tool is missing when you build.

A complete list per language is documented in each profile chapter under the Buffy Reference. The most common tools:

  • protoc — The Protocol Buffers compiler. Required by every language.
  • git — Required by every git profile variant and by Go modules.
  • Per language: go, cargo, mvn, npm, plus their respective protobuf plugins.

To verify the toolchain for a project after configuring it, run:

buffy check

Updating

Re-running the installer updates Buffy to the latest version:

curl -sSL https://pkgs.julian-siebert.de/buffy/install.sh | sh

Uninstalling

Remove the binary:

rm ~/.local/bin/buffy

First Steps with Buffy

This walkthrough creates a small Buffy project, defines a .proto schema, and generates libraries for two languages - Go and Rust - using the git variant so no registry accounts are needed.

Create a project

Create a directory for your project and add the standard layout:

mkdir tomato && cd tomato
mkdir proto .buffy

Define the manifest

Create a Buffy.toml at the project root:

[package]
name = "tomato"
description = "Tomato protocol buffers"
version = "0.1.0"
license = "MIT"
homepage = "https://github.com/example/tomato"
authors = ["Jane Doe <jane@example.com>"]

[source]
path = "proto"

See The Manifest Format for all available fields.

Add a .proto file

Create proto/greeter.proto:

syntax = "proto3";

package greeter;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Configure profiles

Create one profile file per language target.

.buffy/golang.toml:

[golang.git]
module = "github.com/example/tomato-go"
remote = "git@github.com:example/tomato-go.git"
branch = "main"
grpc = true

.buffy/rust.toml:

[rust.git]
name = "tomato"
edition = "2021"
remote = "git@github.com:example/tomato-rs.git"
branch = "main"
repository = "https://github.com/example/tomato-rs"
documentation = "https://github.com/example/tomato-rs"
grpc = true

The filename (e.g., golang.toml) becomes the profile name and shows up in build output. See Profiles Format for the full list of options per language.

Verify the toolchain

Before building, check that all required tools are installed:

buffy check

If anything is missing, Buffy prints an installation hint specific to your platform.

Build

Run Buffy without arguments to build every profile in parallel:

buffy

Each profile produces a complete, self-contained package under target/:

Buffy Reference

The reference covers the details of various areas of Buffy.

The Manifest Format

The Buffy.toml file for each package is called its manifest. It is written in the [TOML] format. It contains metadata that is needed to compile and publish the protocol buffers to all supported language targets.

Every manifest file consists of the following sections:

  • [package] — Defines a package.
    • name — The name of the package.
    • version — The version of the package.
    • description — A description of the package.
    • license — The package license.
    • homepage — URL of the package homepage.
    • authors — The authors of the package.
  • [source] — Configures where .proto files live.
    • path — Path to the directory containing .proto files.

Profile configuration (the per-language publishing targets in .buffy/) is documented separately in the profiles chapter.

The [package] section

The first section in a Buffy.toml is [package].

[package]
name = "tomato"   # the name of the package
version = "0.1.0" # the current version, obeying semver

All fields in this section are required. The metadata defined here is embedded in every published artifact across all language targets (e.g., it ends up in Cargo.toml, the Maven POM, package.json, and the AUTHORS/LICENSE files of generated Go modules).

The name field

The package name is an identifier used to refer to the package. It serves as the default base name for language-specific artifacts (Cargo crate name, Maven artifactId, npm package name, etc.). Profiles may override the name per language.

The name must use only alphanumeric characters or - or _, and cannot be empty.

  • Only ASCII characters are allowed.
  • Use a maximum of 32 characters of length.
[package]
name = "tomato"

The version field

The version field is formatted according to the SemVer specification:

Versions must have three numeric parts: the major version, the minor version, and the patch version.

A pre-release part can be added after a dash such as 1.0.0-alpha. The pre-release part may be separated with periods to distinguish separate components. Numeric components will use numeric comparison while everything else will be compared lexicographically. For example, 1.0.0-alpha.11 is higher than 1.0.0-alpha.4.

A metadata part can be added after a plus, such as 1.0.0+21AF26D3. This is for informational purposes only and is generally ignored.

[package]
# ...
version = "0.1.0"

The version applies to all language targets when publishing. To override the version for a single run (e.g., in CI where it is derived from a Git tag), use the --publish-version flag:

buffy --publish --publish-version 1.2.3

The description field

The description is a short blurb about the package. Registries that display it (such as crates.io for the Rust target or npmjs.com for the JavaScript and TypeScript targets) will show this text on the package page. This should be plain text (not Markdown).

[package]
# ...
description = "Tomato protocol buffers for the salad service"

The license field

The license field contains the SPDX expression of the software license that the package is released under.

The value is interpreted as an SPDX 2.3 license expression. The name must be a known license from the SPDX license list. SPDX expressions support AND and OR operators to combine multiple licenses.

[package]
# ...
license = "MIT OR Apache-2.0"

Using OR indicates the user may choose either license. Using AND indicates the user must comply with both licenses simultaneously. Some examples:

  • MIT
  • MIT OR Apache-2.0
  • LGPL-2.1-only AND MIT AND BSD-2-Clause

When the manifest declares multiple licenses, Buffy generates one LICENSE-<id> file per license plus an index LICENSE file describing the combination. With a single license, a single LICENSE file is generated. The full license text is embedded from the SPDX database.

Custom LicenseRef-* identifiers are not supported.

The homepage field

The homepage field should be a URL to a site that is the home page for your package.

[package]
# ...
homepage = "https://github.com/example/tomato"

The homepage URL is surfaced in language-specific metadata fields where applicable: homepage in Cargo.toml, <url> in the Maven POM, homepage in package.json.

The authors field

The authors field lists the people or organizations that are considered the authors of the package. An optional email address may be included within angled brackets at the end of each author entry.

[package]
# ...
authors = [
    "Jane Doe <jane@example.com>",
    "John Smith",
    "Acme Corp <opensource@acme.com>",
]

Each entry is parsed into a name and an optional email. Both are then forwarded to language-specific metadata: <developer> entries in the Maven POM, the author field in package.json, the authors array in Cargo.toml, and an AUTHORS file in generated Go modules.

If an entry is malformed (e.g., empty, or containing brackets without an email), Buffy reports a diagnostic pointing at the offending entry.

The [source] section

The [source] section tells Buffy where to find the .proto files for code generation.

[source]
path = "proto"

If the section is omitted, the default is used:

[source]
path = "src"

The path field

The relative path (from Buffy.toml) to the directory containing .proto files. Buffy walks this directory recursively and passes every .proto file it finds to protoc.

The path is also used as --proto_path when invoking protoc, so imports between .proto files should be relative to this root.

[source]
path = "proto"

Profiles Format

Profiles configure language-specific publishing targets. They live in the .buffy/ directory next to Buffy.toml, with one TOML file per profile.

my-project/
├── Buffy.toml
├── proto/
│   └── greeter.proto
└── .buffy/
    ├── golang-github.toml
    ├── java.toml
    ├── kotlin.toml
    ├── rust-crates-io.toml
    ├── js.toml
    └── typescript.toml

The filename (without .toml) becomes the profile name. The profile name is used as the directory under target/ where build artifacts are written (e.g., target/golang-github/, target/rust-crates-io/) and as the prefix in progress output.

Profile naming

Profile names must be unique within a project. Buffy reports an error if two files in .buffy/ resolve to the same name (e.g., on case-insensitive filesystems where Rust.toml and rust.toml collide).

The filename has no relationship to the language the profile configures — the language is selected by the table syntax inside the file. A profile called internal-go.toml can configure a golang target, and a project may contain multiple profiles for the same language under different names (for example, one for an internal Git remote and another for a public registry).

File structure

Each profile file selects exactly one language and exactly one publishing variant, using nested-table syntax:

[<language>.<variant>]
# fields specific to the language and variant

For example, a Rust profile that publishes to crates.io:

[rust.crate]
name = "tomato"
edition = "2021"
repository = "https://github.com/example/tomato"
documentation = "https://docs.rs/tomato"
registry = "crates-io"
grpc = true

A Go profile that publishes via Git:

[golang.git]
module = "github.com/example/tomato-go"
remote = "git@github.com:example/tomato-go.git"
branch = "main"
grpc = true
keep = ["README.md"]

A profile file must contain exactly one [<language>.<variant>] table. Multiple variants in the same file are not supported; use multiple profile files instead.

Available languages and variants

LanguageVariantsDefault destination
golanggitA Git remote (Go modules use Git tags)
javamaven_central, gitSonatype Central Portal
kotlinmaven_central, gitSonatype Central Portal
rustcrate, gitcrates.io or another Cargo registry
javascriptnpm, gitnpmjs.org or any npm-compatible registry
typescriptnpm, gitnpmjs.org or any npm-compatible registry
pythonpypi, gitpypi.org

Each language is documented in its own chapter, with the full list of fields per variant.

The git variant

Every language supports a git variant. Instead of publishing to a registry, Buffy commits the generated artifact to a Git repository and tags the commit with v<version>. Consumers depend on the package by its Git URL.

Common fields across all git variants:

  • remote — The Git URL the artifact is pushed to. SSH URLs are recommended; Buffy disables Git’s terminal prompt during operations, so HTTPS URLs work only if credentials are pre-cached.
  • branch — The branch to push to. The previous content is replaced (force-push), with the exception of files listed in keep.
  • keep — A list of paths to fetch from the remote before committing, preserving them across publishes. Useful for human-maintained files such as README.md that should not be overwritten by code generation.

The exact list of fields varies by language because the build artifact itself is language-specific (a Cargo.toml, a Maven POM, a package.json, etc.) and those fields are part of the profile.

Build output

Each profile is built into its own directory under target/:

target/
├── golang/        # output of the `golang.toml` profile
├── java/          # output of the `java.toml` profile
└── ...

The directory is cleared at the start of each build, so all generated content reflects the current run. Buffy automatically adds target/ to a .gitignore at the repository root.

Parallelism

All profiles are built in parallel. With --publish, the publish step is also parallel. If any profile fails, Buffy continues with the others and reports a combined diagnostic at the end of the run, listing every profile that succeeded and every profile that failed with its underlying error.

Environment variables

Most variants require credentials to publish, supplied via environment variables. See Environment Variables for the complete list.

Golang Profiles

The golang profile generates a Go module from your .proto files and publishes it to a Git repository. Go modules are versioned by Git tags, so the consuming side only needs go get <module>@<tag>.

Available variants:

  • git — Push the generated module to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler.
  • protoc-gen-go — Go code generator plugin.
  • protoc-gen-go-grpc — gRPC plugin (only when grpc = true).
  • go — Go toolchain (used for go mod init, go mod tidy, go build).
  • git — Used to commit, tag, and push the generated module.

buffy check verifies that all of these are installed and on the PATH, emitting installation hints if anything is missing.

The git variant

Generates the Go module under target/<profile>/, runs go mod init and go mod tidy to populate go.sum, then commits the result and pushes it to the configured remote with a v<version> tag.

Example

# .buffy/golang.toml
[golang.git]
module = "github.com/example/tomato-go"
remote = "git@github.com:example/tomato-go.git"
branch = "main"
grpc = true
keep = ["README.md"]

Fields

  • module — The Go module path.
  • remote — Git remote URL.
  • branch — Branch to push to.
  • grpc — Whether to generate gRPC service stubs.
  • keep — Files to preserve across publishes.

The module field

The Go module path, as it will appear in go.mod and as consumers will use it in their import statements. Conventionally matches the host and path of the remote.

module = "github.com/example/tomato-go"

This value is passed to protoc-gen-go as --go_opt=module=... so that the generated package paths are rewritten correctly.

The remote field

The Git URL the generated module is pushed to. SSH URLs are recommended because Buffy disables Git’s terminal prompt; HTTPS URLs work only if credentials are pre-cached or supplied via a credential helper.

remote = "git@github.com:example/tomato-go.git"

The branch field

The branch to push to. Buffy force-pushes the generated content to this branch on every publish; the previous content is replaced (with the exception of files listed in keep).

branch = "main"

The grpc field

When true, Buffy invokes protoc-gen-go-grpc in addition to protoc-gen-go, generating service stubs alongside the message types. When omitted or false, only message types are generated.

grpc = true

Default: false.

The keep field

A list of file paths (relative to the repository root) that Buffy fetches from the remote before committing. Useful for human-maintained files like README.md that should outlive the regenerated content.

keep = ["README.md", "docs/usage.md"]

If a listed file does not yet exist on the remote, Buffy logs a notice and skips it instead of failing.

Default: [] (no files preserved).

Example consumer usage

go get github.com/example/tomato-go@v0.1.0
import (
    pb "github.com/example/tomato-go/greeter"
)

Java Profiles

The java profile generates a Maven project from your .proto files using protoc-gen-java and publishes it either to a Git repository or to Maven Central via the Sonatype Central Portal. Consumers depend on the artifact through a normal Maven (or Gradle) coordinate.

Available variants:

  • maven_central — Publish to Maven Central.
  • git — Push the generated Maven project to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler.
  • java — A JDK (11 or newer recommended), used by Maven.
  • mvn — Apache Maven, used to compile and (for maven_central) deploy.
  • gpg — GnuPG, used to sign artifacts. Required for the maven_central variant; not used by the git variant.
  • git — Required only for the git variant.

buffy check verifies that the relevant tools are installed for the configured variant.

The maven_central variant

Generates a Maven project under target/<profile>/, runs mvn compile to verify, signs the artifacts with GPG, and uploads them to the Sonatype Central Portal via the central-publishing-maven-plugin.

The Sonatype namespace must be verified for your account before the first publish (e.g., io.github.<your-github-user> is verified by creating a public repository named after a verification code Sonatype gives you).

Example

# .buffy/java-example.toml
[java.maven_central]
group_id = "io.github.example"
artifact_id = "tomato"
url = "https://github.com/example/tomato"
auto_publish = false
wait_until = "uploaded"

[java.maven_central.scm]
connection = "scm:git:git://github.com/example/tomato.git"
url = "https://github.com/example/tomato"

Fields

  • group_id — Maven group ID.
  • artifact_id — Maven artifact ID.
  • url — Project URL embedded in the POM.
  • scm — Source-control coordinates required by Maven Central.
  • protobuf_version — Pin the protobuf-java runtime version.
  • auto_publish — Auto-release after upload validates.
  • wait_until — How far to wait in the publishing pipeline.

The group_id field

The Maven groupId of the published artifact. Must match a namespace verified for your Sonatype account.

group_id = "io.github.example"

The artifact_id field

The Maven artifactId of the published artifact.

artifact_id = "tomato"

The url field

A project URL written to the <url> tag in the generated POM. Maven Central requires this field to be present.

url = "https://github.com/example/tomato"

The scm section

Source-control coordinates required by Maven Central. Embedded in the <scm> block of the POM.

[java.maven_central.scm]
connection = "scm:git:git://github.com/example/tomato.git"
url = "https://github.com/example/tomato"
  • connection — The SCM connection string, conventionally scm:git:<git-url>.
  • url — A browsable URL for the source repository.

The protobuf_version field

Optional. Pin a specific version of the com.google.protobuf:protobuf-java runtime dependency. If omitted, Buffy queries Maven Central for the latest release version.

protobuf_version = "4.29.3"

The auto_publish field

When true, the Sonatype Central Portal automatically releases the artifact after validation succeeds. When false, the artifact lands in the “Validated” state in the portal and must be released manually.

auto_publish = false

Default: false. For first releases, leave this off so you can manually verify the upload.

The wait_until field

Controls how long the publish step waits before returning. One of:

ValueWaits forTypical duration
uploadedUpload to Sonatype completesseconds
validatedSonatype validation (schema, GPG, etc.)1–3 minutes
publishedIndexing into Maven Central completes10–30 minutes
wait_until = "uploaded"

Default: uploaded.

Required environment variables

VariablePurpose
MAVEN_USERNAMESonatype Central Portal username token
MAVEN_PASSWORDSonatype Central Portal password token
GPG_KEY_IDGPG key ID used to sign the artifacts
GPG_PASSPHRASEPassphrase for the GPG key
GPG_PRIVATE_KEYOptional: armored private key, for CI runners

See Environment Variables for details.

Example consumer usage

<dependency>
  <groupId>io.github.example</groupId>
  <artifactId>tomato</artifactId>
  <version>0.1.0</version>
</dependency>

The git variant

Generates the same Maven project as maven_central, but commits and tags it to a Git repository instead of uploading. No GPG signing, no Sonatype account. Useful for internal sharing or quick prototypes.

Example

# .buffy/java.toml
[java.git]
group_id = "com.example"
artifact_id = "tomato"
url = "https://github.com/example/tomato-java"
remote = "git@github.com:example/tomato-java.git"
branch = "main"
keep = ["README.md"]

[java.git.scm]
connection = "scm:git:git://github.com/example/tomato-java.git"
url = "https://github.com/example/tomato-java"

Fields

  • group_id — as in maven_central.
  • artifact_id — as in maven_central.
  • url — as in maven_central.
  • scm — as in maven_central.
  • protobuf_version — as in maven_central.
  • remote — Git URL the artifact is pushed to.
  • branch — Branch to push to.
  • keep — Files to preserve from the remote.

The remote field

The Git URL the generated Maven project is pushed to. SSH URLs are recommended because Buffy disables Git’s terminal prompt.

remote = "git@github.com:example/tomato-java.git"

The branch field

The branch to push to. Buffy force-pushes on every publish.

branch = "main"

The keep field

A list of file paths (relative to the repository root) to preserve across publishes by checking them out from the remote before committing.

keep = ["README.md"]

Default: [].

Example consumer usage

Maven projects can depend on a Git source via JitPack or by cloning and running mvn install locally. Example with JitPack:

<repositories>
  <repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
  </repository>
</repositories>

<dependency>
  <groupId>com.github.example</groupId>
  <artifactId>tomato-java</artifactId>
  <version>v0.1.0</version>
</dependency>

Kotlin Profiles

The kotlin profile generates a Maven project from your .proto files. Buffy uses protoc-gen-java to generate the message classes and packages them together with the Kotlin standard library so the artifact can be consumed naturally from Kotlin code. Kotlin can call Java protobuf classes directly, so no separate Kotlin code generation is required.

Available variants:

  • maven_central — Publish to Maven Central.
  • git — Push the generated Maven project to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler.
  • java — A JDK (11 or newer recommended).
  • mvn — Apache Maven; the Kotlin compiler is downloaded automatically as a Maven plugin, no separate kotlin binary is required.
  • gpg — GnuPG, for the maven_central variant.
  • git — For the git variant.

The maven_central variant

Generates a Maven project that compiles both Java (from protoc) and Kotlin sources, then publishes the resulting JAR (with sources and Javadoc) to Maven Central via the Sonatype Central Portal.

Example

# .buffy/kotlin-example.toml
[kotlin.maven_central]
group_id = "io.github.example"
artifact_id = "tomato-kotlin"
url = "https://github.com/example/tomato"
auto_publish = false
wait_until = "uploaded"

[kotlin.maven_central.scm]
connection = "scm:git:git://github.com/example/tomato.git"
url = "https://github.com/example/tomato"

Fields

The group_id field

The Maven groupId. Must match a namespace verified for your Sonatype account.

group_id = "io.github.example"

The artifact_id field

The Maven artifactId.

artifact_id = "tomato-kotlin"

The url field

The project URL embedded in the POM <url> tag.

url = "https://github.com/example/tomato"

The scm section

[kotlin.maven_central.scm]
connection = "scm:git:git://github.com/example/tomato.git"
url = "https://github.com/example/tomato"

The protobuf_version field

Optional. Pin a specific version of the com.google.protobuf:protobuf-java runtime dependency. If omitted, Buffy queries Maven Central for the latest release.

protobuf_version = "4.29.3"

The kotlin_version field

Optional. Pin a specific version of the Kotlin compiler and standard library. If omitted, Buffy queries Maven Central for the latest release of org.jetbrains.kotlin:kotlin-stdlib.

kotlin_version = "2.0.21"

The auto_publish field

See auto_publish in Java profiles.

The wait_until field

See wait_until in Java profiles.

Required environment variables

Same as Java.

Example consumer usage

<dependency>
  <groupId>io.github.example</groupId>
  <artifactId>tomato-kotlin</artifactId>
  <version>0.1.0</version>
</dependency>
import io.github.example.tomato.GreeterOuterClass.HelloRequest

val req = HelloRequest.newBuilder().setName("World").build()
println(req.name)

The git variant

Like the Java git variant, but with Kotlin tooling configured in the POM.

Example

# .buffy/kotlin.toml
[kotlin.git]
group_id = "com.example"
artifact_id = "tomato-kotlin"
url = "https://github.com/example/tomato-kotlin"
remote = "git@github.com:example/tomato-kotlin.git"
branch = "main"
keep = ["README.md"]

[kotlin.git.scm]
connection = "scm:git:git://github.com/example/tomato-kotlin.git"
url = "https://github.com/example/tomato-kotlin"

Fields

The same fields as maven_central, plus:

  • remote — Git URL the artifact is pushed to.
  • branch — Branch to push to.
  • keep — Files to preserve from the remote.

These behave identically to the same-named fields in Java profiles.

Rust Profiles

The rust profile generates a Cargo crate from your .proto files using prost and (optionally) tonic for gRPC. The crate is published either to crates.io (or any Cargo-compatible registry) or to a Git repository.

Available variants:

  • crate — Publish to a Cargo registry.
  • git — Push the generated crate to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler.
  • protoc-gen-prost — Rust message generator.
  • protoc-gen-prost-crate — Crate-layout generator.
  • protoc-gen-tonic — gRPC plugin (only when grpc = true).
  • cargo — Rust toolchain, used for cargo build and cargo publish.
  • git — Required only for the git variant.

The protoc-gen-* plugins are installed via Cargo:

cargo install protoc-gen-prost protoc-gen-prost-crate protoc-gen-tonic

The crate variant

Generates a Cargo crate under target/<profile>/, runs cargo build to verify, then runs cargo publish against the configured registry.

Example

# .buffy/rust-example.toml
[rust.crate]
name = "tomato"
edition = "2021"
repository = "https://github.com/example/tomato"
documentation = "https://docs.rs/tomato"
registry = "crates-io"
grpc = true

Fields

The name field

The crate name as it will appear in Cargo.toml and on the registry. Must follow Cargo naming rules (lowercase, hyphens permitted).

name = "tomato"

The library name (i.e. the Rust module name) is derived by replacing hyphens with underscores: tomato-rs becomes tomato_rs.

The edition field

The Rust edition for the generated crate.

edition = "2021"

Common values: "2021", "2024". The edition is written verbatim to the generated Cargo.toml.

The repository field

A URL to the source repository. Embedded in the crate’s Cargo.toml as the repository field.

repository = "https://github.com/example/tomato"

The documentation field

A URL to the crate’s documentation page.

documentation = "https://docs.rs/tomato"

The registry field

The name of the Cargo registry to publish to. The special value "crates-io" targets crates.io directly. Any other value must correspond to a registry configured in the user’s ~/.cargo/config.toml:

# ~/.cargo/config.toml
[registries]
my-registry = { index = "sparse+https://my-kellnr.example.com/api/v1/crates/" }
# .buffy/rust.toml
registry = "my-registry"

Default: "crates-io".

The prost_version field

Optional. Pin the prost runtime version. If omitted, Buffy queries crates.io for the latest release.

prost_version = "0.13"

The tonic_version field

Optional. Pin the tonic runtime version (and tonic-prost for tonic 0.13+). Only used when grpc = true. If omitted, Buffy queries crates.io for the latest release.

tonic_version = "0.13"

The grpc field

When true, Buffy invokes protoc-gen-tonic and includes tonic and tonic-prost in the crate’s dependencies.

grpc = true

Default: false.

Required environment variables

VariablePurpose
CARGO_REGISTRY_TOKENToken for the configured registry (publish auth)

For non-crates-io registries, the index URL must be configured in ~/.cargo/config.toml as shown above.

Example consumer usage

cargo add tomato
#![allow(unused)]
fn main() {
use tomato::greeter::HelloRequest;

let req = HelloRequest { name: "World".into() };
println!("{}", req.name);
}

The git variant

Generates the same crate as crate, but commits and tags it to a Git repository instead of uploading to a registry. Useful when you want to distribute internally without setting up a registry, or to give consumers a reproducible Git pin.

Example

# .buffy/rust.toml
[rust.git]
name = "tomato"
edition = "2021"
remote = "git@github.com:example/tomato-rs.git"
branch = "main"
repository = "https://github.com/example/tomato-rs"
documentation = "https://github.com/example/tomato-rs"
grpc = true
keep = ["README.md"]

Fields

The same fields as crate (except registry, which doesn’t apply), plus:

  • remote — Git URL the crate is pushed to.
  • branch — Branch to push to.
  • keep — Files to preserve from the remote.

The remote field

remote = "git@github.com:example/tomato-rs.git"

The branch field

branch = "main"

The keep field

keep = ["README.md"]

Default: [].

Example consumer usage

# in the consumer's Cargo.toml
[dependencies]
tomato = { git = "https://github.com/example/tomato-rs", tag = "v0.1.0" }

JavaScript Profiles

The javascript profile generates a CommonJS JavaScript library from your .proto files using protoc-gen-js and (optionally) gRPC-Web for browser-compatible gRPC. The package is published either to npm or to a Git repository.

Available variants:

  • npm — Publish to npm or another npm-compatible registry.
  • git — Push the generated package to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler.
  • protoc-gen-js — JavaScript code generator.
  • protoc-gen-grpc-web — gRPC-Web plugin (only when grpc = true).
  • node — Node.js runtime.
  • npm — npm CLI, used to install dependencies and publish.
  • git — Required only for the git variant.

The npm variant

Generates a JavaScript package under target/<profile>/, validates the package.json by running npm install and npm publish --dry-run, then publishes via npm publish.

Example

# .buffy/js-example.toml
[javascript.npm]
name = "@example/tomato"
registry = "https://registry.npmjs.org/"
access = "public"
repository = "https://github.com/example/tomato"
homepage = "https://github.com/example/tomato"
grpc = true

Fields

  • name — npm package name.
  • registry — Registry URL.
  • access"public" or "restricted" for scoped packages.
  • repository — Repository URL embedded in the package.
  • homepage — Override the global homepage.
  • grpc — Generate gRPC-Web service stubs.

The name field

The npm package name. Scoped packages (e.g. @example/tomato) are supported.

name = "@example/tomato"

The registry field

Optional. The npm registry URL to publish to. Examples:

  • "https://registry.npmjs.org/" — the public npm registry.
  • "https://npm.pkg.github.com/" — GitHub Packages.
  • "http://localhost:4873/" — a local Verdaccio instance for testing.
registry = "https://registry.npmjs.org/"

Default: "https://registry.npmjs.org/".

The access field

Optional. Controls the access level for scoped packages:

  • "public" — the package is publicly readable.
  • "restricted" — the package requires authentication to read (paid npm feature).

For unscoped packages, the value has no effect.

access = "public"

Default: "public".

The repository field

A URL to the source repository, embedded in package.json as the repository.url field.

repository = "https://github.com/example/tomato"

The homepage field

Optional. Overrides the package’s homepage URL. If omitted, the value from the global [package] section’s homepage field is used.

homepage = "https://example.com/tomato"

The grpc field

When true, Buffy invokes protoc-gen-grpc-web to generate browser-compatible gRPC-Web service stubs and includes grpc-web in the package’s dependencies.

grpc = true

Default: false.

Required environment variables

VariablePurpose
NPM_TOKENAuth token for the configured registry

The token can come from npm token create (npm) or the corresponding mechanism for your registry (Verdaccio, GitHub Packages, etc.).

Example consumer usage

npm install @example/tomato
const { HelloRequest } = require("@example/tomato/greeter_pb");

const req = new HelloRequest();
req.setName("World");
console.log(req.getName());

The git variant

Generates the same JavaScript package as npm, but commits and tags it to a Git repository instead of uploading to a registry.

Example

# .buffy/javascript.toml
[javascript.git]
name = "@example/tomato"
remote = "git@github.com:example/tomato-js.git"
branch = "main"
repository = "https://github.com/example/tomato-js"
grpc = true
keep = ["README.md"]

Fields

The same fields as npm (except registry and access), plus:

  • remote — Git URL the package is pushed to.
  • branch — Branch to push to.
  • keep — Files to preserve from the remote.

The remote field

remote = "git@github.com:example/tomato-js.git"

The branch field

branch = "main"

The keep field

keep = ["README.md"]

Default: [].

Example consumer usage

npm packages can be installed directly from a Git source:

npm install git+ssh://git@github.com/example/tomato-js.git#v0.1.0

TypeScript Profiles

The typescript profile generates a TypeScript library from your .proto files using ts-proto and compiles it with tsc before publishing. The package contains both compiled JavaScript and .d.ts declarations.

Available variants:

  • npm — Publish to npm or another npm-compatible registry.
  • git — Push the generated package to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler.
  • protoc-gen-ts_proto — TypeScript generator from ts-proto.
  • node — Node.js runtime.
  • npm — npm CLI, used for install, build, and publish.
  • tsc — TypeScript compiler.
  • git — Required only for the git variant.

Install the TypeScript tooling globally:

npm install -g ts-proto typescript

The npm variant

Generates a TypeScript package under target/<profile>/, runs npm install to fetch dependencies, runs tsc via npm run build to produce dist/, then publishes via npm publish.

Example

# .buffy/ts-example.toml
[typescript.npm]
name = "@example/tomato"
registry = "https://registry.npmjs.org/"
access = "public"
repository = "https://github.com/example/tomato"
grpc = true

Fields

  • name — npm package name.
  • registry — Registry URL.
  • access"public" or "restricted" for scoped packages.
  • repository — Repository URL embedded in the package.
  • homepage — Override the global homepage.
  • grpc — Generate gRPC service stubs.

The semantics of these fields are the same as in the JavaScript npm variant. The differences in the generated output are in the code itself, not the configuration:

  • TypeScript output uses ES interfaces and types instead of JavaScript classes.
  • gRPC stubs use nice-grpc (Node.js and browser compatible) instead of grpc-web.
  • The package includes a tsc build step before publishing, so consumers receive precompiled JavaScript and .d.ts files.

Required environment variables

VariablePurpose
NPM_TOKENAuth token for the configured registry

Example consumer usage

npm install @example/tomato
import { HelloRequest } from "@example/tomato";

const req: HelloRequest = { name: "World" };
console.log(req.name);

The git variant

Generates the same TypeScript package as npm, but commits and tags it to a Git repository instead of uploading to a registry.

Example

# .buffy/typescript.toml
[typescript.git]
name = "@example/tomato"
remote = "git@github.com:example/tomato-ts.git"
branch = "main"
repository = "https://github.com/example/tomato-ts"
grpc = true
keep = ["README.md"]

Fields

The same fields as npm (except registry and access), plus:

  • remote — Git URL the package is pushed to.
  • branch — Branch to push to.
  • keep — Files to preserve from the remote.

These fields behave identically to the same-named fields in JavaScript profiles.

Example consumer usage

npm install git+ssh://git@github.com/example/tomato-ts.git#v0.1.0

Python Profiles

The python profile generates a Python package from your .proto files using the grpc_tools.protoc plugin (which is the official Python code generator, distributed as part of grpcio-tools). The package is published either to PyPI (or any PyPI-compatible registry) or to a Git repository.

Available variants:

  • pypi — Publish to PyPI or another PEP 503 registry.
  • git — Push the generated package to a Git remote.

Required tools

  • protoc — Protocol Buffers compiler (used via grpcio-tools).
  • python3 — Python 3.9 or newer.
  • grpcio-tools — Python module providing the protoc-based code generator.
  • build — PEP 517 build module, used to produce sdist and wheel.
  • twine — PyPI upload tool (only for the pypi variant).
  • git — For the git variant.

Install the Python tooling:

pip install grpcio-tools build twine

The pypi variant

Generates a Python package under target/<profile>/, runs python -m build to produce sdist and wheel under dist/, then uploads them with twine.

Example

# .buffy/python.toml
[python.pypi]
name = "tomato-proto"
repository_url = "https://upload.pypi.org/legacy/"
repository = "https://github.com/example/tomato"
grpc = true

Fields

  • name — PyPI package name. Hyphens are converted to underscores for the importable module name.
  • repository_url — The PyPI-compatible upload endpoint. Default is the public PyPI; use https://test.pypi.org/legacy/ for testing.
  • repository — URL to the source repository.
  • homepage — Optional override of the global homepage.
  • grpc — When true, generates gRPC service stubs via --grpc_python_out=....
  • protobuf_version — Optional pin for the protobuf runtime dependency.
  • grpcio_version — Optional pin for the grpcio runtime dependency.

Required environment variables

VariablePurpose
PYPI_TOKENAPI token for the configured PyPI

Tokens are created at https://pypi.org/manage/account/token/ (or the equivalent path on a private registry). Include the pypi- prefix.

Example consumer usage

pip install tomato-proto
from tomato_proto import greeter_pb2

req = greeter_pb2.HelloRequest(name="World")
print(req.name)

The git variant

Generates the same package as pypi, but commits and tags it to a Git repository.

Example

[python.git]
name = "tomato-proto"
remote = "git@github.com:example/tomato-py.git"
branch = "main"
repository = "https://github.com/example/tomato-py"
grpc = true
keep = ["README.md"]

Example consumer usage

pip install git+https://github.com/example/tomato-py@v0.1.0

Environment Variables

Buffy reads credentials and a few configuration values from environment variables rather than from Buffy.toml or the profile files. This keeps secrets out of source control and matches the conventions of the underlying tooling (Cargo, Maven, npm).

For local development, Buffy auto-loads a .env file from the current directory via dotenvy. For CI, set the variables through your provider’s secret store (GitHub Actions secrets, GitLab CI variables, etc.).

Quoting in .env files

dotenvy follows shell-like rules. Values containing $, #, whitespace, or other special characters should be wrapped in single quotes to disable variable expansion and escaping:

GPG_PASSPHRASE='HD$qOdHYiG#jGUCpNJhzSSx5W'
NPM_TOKEN='npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

Double-quoted values allow $VAR expansion, which can silently corrupt secrets that contain a $.

Variables by language

Java and Kotlin

Used by the maven_central variant; not required for the git variant.

VariableRequiredPurpose
MAVEN_USERNAMEyesSonatype Central Portal username token
MAVEN_PASSWORDyesSonatype Central Portal password token
GPG_KEY_IDyesGPG key ID used for signing artifacts
GPG_PASSPHRASEoptionalPassphrase for the GPG key (omit if the key has no passphrase)
GPG_PRIVATE_KEYoptionalArmored private key, imported on each run (intended for CI)

The MAVEN_USERNAME / MAVEN_PASSWORD pair is generated as a token from your account at central.sonatype.com under Profile → User Token.

GPG_PRIVATE_KEY is intended for CI runners that don’t have a persistent keyring. When set, Buffy writes the key to a temporary file, runs gpg --import, and removes the file. Do not set this on machines that already have the key in their local keyring.

Rust

Used by the crate variant; not required for the git variant.

VariableRequiredPurpose
CARGO_REGISTRY_TOKENyesAPI token for the configured Cargo registry

For crates.io, generate the token under Account → API Tokens. For self-hosted registries (e.g. Kellnr), use the token-issuing mechanism of the registry. The registry index URL must additionally be configured in ~/.cargo/config.toml:

[registries]
my-registry = { index = "sparse+https://my-kellnr.example.com/api/v1/crates/" }

JavaScript and TypeScript

Used by the npm variant; not required for the git variant.

VariableRequiredPurpose
NPM_TOKENyesAuth token for the configured registry

For the public npm registry, generate the token via npm token create or in the npmjs.com web UI. For other registries (Verdaccio, GitHub Packages, etc.), use the registry’s token-issuing mechanism.

Buffy writes a temporary .npmrc to the build directory containing the token and the configured registry URL, then removes it after publishing. Existing .npmrc files in your home directory are not used.

Golang

The golang profile only supports a git variant, which uses Git itself for authentication. Configure your SSH agent or HTTPS credentials via Git as usual; Buffy does not read any environment variables for this profile.

Variables by use case

Local development

The minimal .env to publish from a developer machine across all targets:

# Java / Kotlin (Maven Central)
MAVEN_USERNAME='your-sonatype-username'
MAVEN_PASSWORD='your-sonatype-token'
GPG_KEY_ID='YOURGPGKEYID0123456789ABCDEF'
GPG_PASSPHRASE='your-gpg-passphrase'

# Rust (crates.io or other Cargo registry)
CARGO_REGISTRY_TOKEN='your-cargo-token'

# JavaScript / TypeScript (npm)
NPM_TOKEN='your-npm-token'

For Git-only profiles, no environment variables are required — Git uses your SSH agent.

CI

In CI, additionally set GPG_PRIVATE_KEY so that the runner can import the signing key on each run:

GPG_PRIVATE_KEY='-----BEGIN PGP PRIVATE KEY BLOCK-----
...
-----END PGP PRIVATE KEY BLOCK-----'

CI secret stores typically support multiline values directly. If yours does not, base64-encode the key and decode it in your pipeline before exposing it as GPG_PRIVATE_KEY.

Behavior

When a required variable is missing or set to an empty string, Buffy emits a diagnostic identifying the variable, the profile that requires it, and a short hint on how to obtain it. The build does not proceed past this point.

Variables are only consulted at the moment they are needed. For instance, buffy check does not require any of these to be set, since it does not publish anything.

Command-Line Interface

The buffy CLI reads Buffy.toml and the profiles in .buffy/, builds all configured language targets in parallel, and (with --publish) pushes them to their respective registries or remotes.

Synopsis

buffy [OPTIONS] [COMMAND]

When invoked without a subcommand, Buffy builds every profile in .buffy/. If the .buffy/ directory does not exist or contains no profiles, Buffy exits with a notice and no error.

Commands

  • buffy — Build all configured profiles.
  • buffy check — Verify that required tools are installed.

buffy

Build (and optionally publish) every profile.

buffy

Each profile is built independently and in parallel. If any profile fails, Buffy continues building the others and reports a combined diagnostic at the end, listing every profile that succeeded and every profile that failed with its underlying error.

With --publish, every profile that built successfully is then published to its configured destination.

buffy check

Verify that all required external tools are installed and reachable on the PATH for the configured profiles. No code is generated and nothing is built.

buffy check

The check is profile-aware: it only verifies the tools needed by the configured profiles. For instance, a project with only a rust profile does not require mvn to be installed.

If a tool is missing, Buffy emits a diagnostic with the tool name and an installation hint specific to common platforms. See the per-language profile chapter for the full list of tools each variant requires.

Options

-p, --publish

Publish every profile after a successful build.

buffy --publish

Each profile is published to the destination configured in its .buffy/<name>.toml file:

  • crate for Rust → the configured Cargo registry (defaulting to crates.io).
  • npm for JavaScript / TypeScript → the configured npm registry.
  • maven_central for Java / Kotlin → the Sonatype Central Portal.
  • git for any language → the configured Git remote, with a v<version> tag.

If a required environment variable is missing, Buffy reports a diagnostic with the variable name and a description of how to obtain it. See Environment Variables.

--publish-version <VERSION>

Override the version from Buffy.toml for this run.

buffy --publish --publish-version 1.2.3

The value must be a valid SemVer version. This is intended for CI pipelines that derive the release version from a Git tag rather than committing it into Buffy.toml.

The override applies only to the current invocation; the file on disk is not modified.

--verbose

Print the full output (both stdout and stderr) of every external tool Buffy invokes.

buffy --verbose

By default, Buffy streams tool output to your terminal as it arrives, prefixed with the profile name and the program being run. With --verbose, the unfiltered streams are shown, which is useful when debugging:

  • Maven build failures, where the relevant [ERROR] lines are mixed in with a long [INFO] log on stdout.
  • npm install issues that print resolution diagnostics on stdout.
  • Cargo warnings about deprecated APIs.

Without --verbose, Buffy still surfaces the relevant error context when a command fails.

-h, --help

Print a short help message.

-V, --version

Print the Buffy version.

Exit codes

  • 0 — All profiles built (and, with --publish, published) successfully.
  • 1 — One or more profiles failed. The diagnostic at the end of the run lists every failure.

Examples

Build all profiles:

buffy

Verify the toolchain without building:

buffy check

Build and publish using the version from Buffy.toml:

buffy --publish

Override the version (typical CI usage):

buffy --publish --publish-version "${GITHUB_REF_NAME#v}"

Get verbose output for debugging:

buffy --publish --verbose

Frequently Asked Questions

How is Buffy different from buf?

buf is a feature-rich linter, formatter, and code generator that orchestrates protoc plugins. Buffy is a build and publishing tool: it cares less about lint rules and breaking-change detection, and more about turning a set of .proto files into a publishable Go module, Cargo crate, npm package, or Maven artifact in one command. The two tools are complementary! You can run buf lint and buf breaking in CI alongside buffy --publish.

Why generate per-language packages instead of using a Buf Schema Registry?

The Buf Schema Registry is an excellent option if you control both the producer and the consumer side and want strong centralization. Buffy is for teams that need protocol buffer libraries to land in the native package ecosystem of each language; so consumers depend on a normal Cargo crate or Maven artifact and don’t have to learn anything new.

Can I add a new language target?

Not as configuration. Language targets are part of Buffy’s source code. The existing targets share a common skeleton (a profile, a code generator, a build step, a publish step), so adding one is straightforward. See the existing modules under targets/ in the Buffy repository for templates.

Why does the --publish step push with --force?

Buffy treats the published artifact’s repository (or the registry version) as a derived output: every release is rebuilt from scratch. Hand-edits inside a generated repository would be overwritten on the next publish, so Buffy force-pushes to make the destination an exact mirror of the generated content. Files listed in the keep field of git profiles are preserved across publishes.

A profile failed but the others succeeded - what happens?

Buffy continues building the remaining profiles and reports a combined diagnostic at the end, listing every profile that succeeded and every profile that failed with its underlying error. The successful profiles are not rolled back; if the failure occurred during --publish, the artifacts that made it out are already live.

Can I use Buffy with private registries?

Yes:

  • Cargo — Configure the registry index in ~/.cargo/config.toml and set registry = "<name>" in the profile.
  • npm — Set the registry field in the profile to your registry’s URL (e.g., a Verdaccio instance, GitHub Packages, or AWS CodeArtifact).
  • Maven — The maven_central variant is hard-wired to the Sonatype Central Portal. For private Maven repositories (Nexus, Artifactory), use the git variant for now and consume from the resulting repository, or open an issue to discuss adding a generic maven variant.

Why doesn’t buffy check need network access?

buffy check only verifies that the local toolchain is installed. It does not invoke protoc, query crates.io for versions, or talk to any registry. This makes it suitable as a fast pre-commit hook or CI gate.

How do I pin dependency versions in generated artifacts?

Each language’s profile has optional fields for pinning runtime versions: protobuf_version for Java/Kotlin, prost_version and tonic_version for Rust, etc. When omitted, Buffy queries the language’s registry for the latest release version.

What happens if Buffy.toml and a profile both define the same field?

Profiles override the manifest where they overlap. For example, the package-level homepage is the default, but the JavaScript profile’s homepage field can override it for the JavaScript package only. Most profile fields don’t overlap with the manifest; they configure language-specific concerns.

Glossary

artifact

The build output of a single profile - a Go module, Cargo crate, Maven JAR, or npm package, produced under target/<profile-name>/. With --publish, the artifact is the thing that gets uploaded to a registry or pushed to a Git remote.

manifest

The Buffy.toml file at the root of a Buffy project. Contains the language-independent metadata (package name, version, license, authors). See The Manifest Format.

package

The unit of code Buffy operates on: a directory containing a Buffy.toml, a directory of .proto files (the source), and zero or more profiles under .buffy/. A package produces one artifact per profile.

profile

A language-and-variant configuration in the .buffy/ directory. Each profile is a TOML file selecting one language (e.g., rust, java) and one publishing variant (e.g., crate, maven_central, git). The filename without .toml becomes the profile name. See Profiles Format.

profile name

The filename of a profile in .buffy/ without the .toml extension. Profile names appear as the subdirectory under target/ and as the prefix in build output. They must be unique within a project.

registry

A central host that distributes packages for a language. Examples: crates.io for Rust, npm for JavaScript and TypeScript, Maven Central for Java and Kotlin. Buffy publishes to a registry via the language’s native publishing variant (crate, npm, maven_central).

source

The directory of .proto files configured under the [source] section of Buffy.toml. Buffy walks this directory recursively and passes every .proto file it finds to protoc. Defaults to src if the section is omitted.

target

The Buffy term for one published output (a Cargo crate, a Maven artifact, an npm package). Corresponds to one profile. Not to be confused with the target/ directory, which holds the generated content for every target.

variant

The publishing destination for a profile, selected as the second part of the nested table name [<language>.<variant>]. Each language defines which variants it supports. For example, rust supports crate and git, while golang supports only git. See the per-language profile chapters.