# connect-rust serves Connect, gRPC and gRPC-Web from one Tower stack

> connect-rust is a Tower-based Rust implementation of the Connect protocol that also speaks gRPC and gRPC-Web over HTTP with binary or JSON protobuf messages. It suits a Rust service that wants one HTTP surface for several client families, and its API is still pre-1.0.

**connectrpc/connect-rust** — An implementation of the ConnectRPC, gRPC, and gRPC-Web for Rust

- Repository: https://github.com/connectrpc/connect-rust
- Stars: 531 · Forks: 69
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/connectrpc-connect-rust

## One Tower service answers three wire protocols

The architecture is a single HTTP surface that speaks three dialects of the same idea. The project is a Tower-based Rust implementation of ConnectRPC, serving Connect, gRPC and gRPC-Web clients over HTTP with binary or JSON protobuf messages. The choice of Tower as the base is what makes it portable. The runtime is built on `tower::Service`, making it framework-agnostic, and it integrates with any tower-compatible HTTP framework including Axum, Hyper and others. So the service is not tied to one server framework: you write a Tower service, mount it, and the protocol handling sits on top. The workspace manifest confirms the dependency set, with `tower` at 0.5 with the util feature, `tower-http` at 0.6 with catch-panic, the `http` and `http-body` crates at 1, and an async runtime built on `tokio` 1, `tokio-stream`, `tokio-util`, `futures` and `async-trait`.

## The workspace is six published crates, not one

The repository root is a Cargo workspace with a set of members, and the split tells you what the project considers separate concerns. There is `connectrpc`, the Tower-based runtime library implementing the Connect protocol, and `connectrpc-codegen`, which backs the `protoc` plugin. There is `connectrpc-build` for `build.rs` integration, so code can be generated at build time rather than checked in. There is `connectrpc-health`, which provides the standard `grpc.health.v1.Health` service, and the documentation says exactly what it is for: `grpc_health_probe`, kubelet gRPC probes and service-mesh health checks. There is `connectrpc-reflection`, the standard gRPC server reflection service for `grpc.reflection.v1` and `v1alpha`, so `grpcurl`, `buf curl`, Postman and `grpcui` can discover and call your services. And there is `connectrpc-types`, the shared `google.rpc` message types, being `Status`, `Code` and the standard error details such as `BadRequest`, `ErrorInfo` and `RetryInfo`, so you can attach them to a `ConnectError` rather than inventing your own error payload. The workspace also includes a `conformance` member and the examples.

## Conformance is the measurement, and the numbers are large

The project reports its standing in a way that is unusual and useful, because it names the test suite rather than claiming quality. The status line says the project is pre-1.0, that the API surface is settling but may shift in 0.x, and that the runtime is production-quality, backed by passing the full ConnectRPC conformance suite with 3,600 server and 6,872 client tests across the three protocols. Those are protocol conformance counts, not performance numbers, and the distinction matters. Conformance tells you that a given server implementation answers correctly for a large set of defined cases across Connect, gRPC and gRPC-Web. It does not tell you anything about latency, throughput or how the implementation behaves under a pathological client. The repository root has a `conformance/` directory, so the suite is part of the tree rather than an external dependency.

## Code generation has two workflows, and the mount point has to be regenerated

Setting up the codegen is the part with real decisions in it. Two workflows are supported and both produce the same runtime API, so the choice is about your build pipeline. The recommended one for checked-in code is `buf generate`, which runs two codegen plugins, `protoc-gen-buffa` for message types and `protoc-gen-connect-rust` for service stubs, plus `protoc-gen-buffa-packaging` twice to assemble the `mod.rs` module tree for each output directory. The codegen plugins are invoked per-file and only the packaging plugin needs `strategy: all`. The configuration that ties it together is a `buf.gen.yaml`:

```yaml
version: v2
plugins:
  - local: protoc-gen-buffa
    out: src/generated/buffa
    opt: [views=true, json=true]
  - local: protoc-gen-buffa-packaging
    out: src/generated/buffa
    strategy: all
  - local: protoc-gen-connect-rust
    out: src/generated/connect
    opt: [buffa_module=crate::proto]
  - local: protoc-gen-buffa-packaging
    out: src/generated/connect
    strategy: all
    opt: [filter=services]
```

The `buffa_module=crate::proto` option tells the service-stub generator where you mounted the buffa output, and for a method input type `greet.v1.GreetRequest` it emits `crate::proto::greet::v1::GreetRequest`. The second packaging invocation uses `filter=services` so the connect tree's `mod.rs` only includes files that actually have service stubs in them. Changing the mount point requires regenerating, which the documentation states plainly.

## Three ways to get the plugin, and one of them needs no local install

The `protoc-gen-connect-rust` plugin can be obtained in three ways, and the choice affects your build's reproducibility. The first is a pre-built binary from the GitHub release, with binaries for Linux on x86_64 and aarch64, macOS on x86_64 and aarch64, and Windows on x86_64. The download block in the documentation is thorough about verification, fetching the binary, a `.sig` and a `.pem`, and a checksum file, then checking the SHA-256 and either the GitHub-native attestation or the cosign signature directly before installing to `/usr/local/bin`. The second is building from source with `cargo install --locked connectrpc-codegen`, which pulls the latest published codegen crate from crates.io and installs the binary into `$CARGO_HOME/bin`. The third is the Buf Schema Registry remote plugin, published as `buf.build/connectrpc/rust` with versions tracking connect-rust releases, where no local install is needed because you replace the `local: protoc-gen-connect-rust` entry with a `remote: buf.build/connectrpc/rust:v0.9.0` one. The message types themselves come from the `buffa` project, whose release page offers binaries or a cargo install.

## The generated modules are mounted by hand into your crate

The output of codegen is not automatically part of your crate, and the mounting is explicit. The documented result in `src/lib.rs` is:

```rust
#[path = "generated/buffa/mod.rs"]
pub mod proto;
#[path = "generated/connect/mod.rs"]
pub mod connect;
```

Two path attributes and two module declarations, which is the whole integration on the Rust side. There is a constraint underneath it that is worth reading before you pin versions. The workspace manifest states that the 0.9.x floor on the underlying protobuf crate is hard rather than a preference, for two reasons: 0.9.0 widens `types::put_len_delimited_header` and the `map_codec::field_len` accumulator to `u64`, so code emitted by a 0.8.x codegen does not compile against a 0.9 runtime, and the codegen paths call `buffa_codegen::decode_request` and `tooling_decode_options`, which arrived in 0.9.1. The manifest also notes that the regen baseline for the checked-in generated code is the buffa version in Cargo.lock rather than that floor, so bumping the lock and regenerating is the supported path.

## Where a pre-1.0 RPC runtime is the wrong bet

Two limits are worth stating. The first is the stability of the surface, and the project says it itself: the API is pre-1.0, the surface is settling, and it may shift in 0.x. For an internal service that you own end to end, that is a manageable risk, because the breakage and the fix are both in your repository. For a library that other teams call, or a service whose clients you do not control, an API that can change under a minor version is a real cost, and the migration work falls on whoever upgrades. The second is the toolchain, which is heavier than a plain Rust crate. The workspace pins the edition to 2024 and the rust-version to 1.88, the MSRV is Rust 1.88 declared on the workspace and verified in CI, and the codegen path involves a `protoc` plugin, a separate `buffa` generator, and a packaging step, with a buf configuration file. The alternative to weigh against is tonic, the gRPC-focused Rust implementation, and the difference in approach is exactly the surface area. tonic speaks gRPC, and gRPC-Web and Connect are things you add with additional layers. connect-rust speaks all three from one stack, so a browser client and a native gRPC client hit the same service, and the price is the wider pre-1.0 surface and the codegen pipeline. If your clients are all gRPC, tonic is the smaller commitment. If one of them is a browser, connect-rust removes a translation layer that you would otherwise have to build.

## Conclusion

connect-rust is worth adopting for a Rust service that has to be reachable by more than one client family, because one Tower stack serves Connect, gRPC and gRPC-Web with the same protobuf messages, and because the workspace ships the health service, the reflection service and the shared google.rpc error types rather than making you assemble them. It is a poor fit if you need a stable API, because the project states plainly that it is pre-1.0 and that the API surface is settling but may shift in 0.x, and a poor fit if your clients are gRPC-only and you have no browser clients, since then the extra protocol coverage buys you nothing. Before adopting it, read the user guide for the code generation workflow you intend to use, check that the buffa version in Cargo.lock matches the one your generated code was produced with, and note that the last push to the repository was on 2026-10-22 with the most recent release v0.9.1 published on 2026-09-21.

## FAQ

### What protocols does connect-rust serve?

It is a Tower-based Rust implementation of ConnectRPC that serves Connect, gRPC and gRPC-Web clients over HTTP with binary or JSON protobuf messages. The runtime is built on tower::Service, so it is framework-agnostic and integrates with any tower-compatible HTTP framework including Axum and Hyper.

### How do I generate the Rust code for a connect-rust service?

Two workflows are supported. The recommended one for checked-in code is buf generate, which runs protoc-gen-buffa for message types, protoc-gen-connect-rust for service stubs and protoc-gen-buffa-packaging twice to assemble the module tree. The alternative is connectrpc-build, which generates the code at build time from build.rs.

### Do I have to install the protoc plugin locally?

No. The plugin is published on the Buf Schema Registry as buf.build/connectrpc/rust with versions tracking connect-rust releases, so you can replace the local: protoc-gen-connect-rust entry with remote: buf.build/connectrpc/rust:v0.9.0. You can also download a signed pre-built binary, or run cargo install --locked connectrpc-codegen to build it from source.

### Is connect-rust ready for production use?

The project states it is pre-1.0, that the API surface is settling but may shift in 0.x, and that the runtime is production-quality, passing the full ConnectRPC conformance suite with 3,600 server and 6,872 client tests across the three protocols. The minimum supported Rust version is 1.88.

## Sources

- [connectrpc/connect-rust on GitHub](https://github.com/connectrpc/connect-rust)
- [Issues](https://github.com/connectrpc/connect-rust/issues)
- [License: Apache-2.0](https://github.com/connectrpc/connect-rust/blob/main/LICENSE)
- [README](https://github.com/connectrpc/connect-rust/blob/main/README.md)
- [Releases](https://github.com/connectrpc/connect-rust/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/connectrpc-connect-rust
