redb.Route.Grpc 3.7.0

Prefix Reserved
dotnet add package redb.Route.Grpc --version 3.7.0
                    
NuGet\Install-Package redb.Route.Grpc -Version 3.7.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="redb.Route.Grpc" Version="3.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="redb.Route.Grpc" Version="3.7.0" />
                    
Directory.Packages.props
<PackageReference Include="redb.Route.Grpc" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add redb.Route.Grpc --version 3.7.0
                    
#r "nuget: redb.Route.Grpc, 3.7.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package redb.Route.Grpc@3.7.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=redb.Route.Grpc&version=3.7.0
                    
Install as a Cake Addin
#tool nuget:?package=redb.Route.Grpc&version=3.7.0
                    
Install as a Cake Tool

redb.Route.Grpc

gRPC transport for redb.Route. A gRPC method address is a route: the consumer registers /package.Service/Method as a path route on the shared Kestrel host, so many methods live on one port as ordinary routes with their own ids, policies and metrics. The producer calls any method address with a GrpcChannel.

NuGet License: Apache 2.0

Installation

dotnet add package redb.Route.Grpc

Usage

using redb.Route.Grpc;

// One port, three methods — three routes.
From(GrpcDsl.Listen("0.0.0.0:5001").Method("/identity.v1.Identity/Token"))
    .RouteId("grpc-token")
    .To("direct-vm://identity-token");

From(GrpcDsl.Listen("0.0.0.0:5001").Method("/identity.v1.Identity/Introspect"))
    .RouteId("grpc-introspect")
    .To("direct-vm://identity-introspect");

// The built-in generic service (headers + payload envelope), unchanged.
From(GrpcDsl.Listen("0.0.0.0:5001"))
    .Process(async (exchange, ct) => exchange.Message.SetBody(Handle(exchange.Message.GetBody<byte[]>())));

// Client.
From("direct://send")
    .To(GrpcDsl.Call("grpc-service:5001")
        .Method("/identity.v1.Identity/Token")
        .Plaintext()
        .Deadline(5000));

How it works

The connector implements the gRPC wire protocol itself (GrpcWire) instead of taking the Grpc.AspNetCore server stack, and serves on the same SharedHttpServerManager as the HTTP, AS2 and SOAP transports. That server stack would need AddGrpc() / MapGrpcService() wired into an ASP.NET host before it is built, which forces one gRPC endpoint per port, a host restart on hot-reload, and a header-based route selector instead of the method address. Owning the framing keeps a gRPC route what every other route is: a URI, an endpoint, a consumer.

On the wire: [1 byte compressed-flag][4 bytes length BE][message], content-type application/grpc, HTTP status always 200, the real outcome in the grpc-status / grpc-message trailers, caller deadline in grpc-timeout.

Compression

Gzipped requests are always accepted and inflated (the size limit is re-checked after inflation, so a small frame cannot expand into an arbitrarily large buffer). Replies are compressed only with .Compression(GrpcCompression.Gzip) and only when the caller advertised gzip in grpc-accept-encoding; we always advertise identity,gzip back. An unknown codec is answered with Unimplemented rather than a parse failure. On the client side .Compression(...) gzips outgoing requests.

Statuses

A route answers with a status the client actually sees:

Source on Out Result
redbGrpc.StatusCode (+ redbGrpc.StatusDetail) used as-is
status.code — what every controller dispatcher writes mapped (401 → Unauthenticated, 403 → PermissionDenied, 404 → NotFound, 409 → AlreadyExists, 429 → ResourceExhausted, 503 → Unavailable, 504 → DeadlineExceeded, other 5xx → Internal)
neither OK
unhandled exception RpcException keeps its status; cancellation splits into DeadlineExceeded / Cancelled; anything else is Internal

redbGrpc.Trailer.* headers become response trailers. A non-OK status is sent trailers-only, because gRPC clients discard the payload of a failed call. suppressStatusMapping=true restores the old always-OK-plus-error-document behaviour.

Typed .proto services

Envelope=Auto (default) wraps only the built-in address in the generic RedbMessage; any other address receives the caller's protobuf bytes untouched. So a client generated from a real .proto can call a redb route with no generated server stubs on our side — the route decodes the message with its own codec. .Envelope(GrpcEnvelopeMode.Message | Raw) forces either behaviour.

Streaming

An IAsyncEnumerable reply body is written one frame per yield — the framework's own streaming shape, the same one the HTTP consumer honours for chunked and SSE replies. Enabled by default on the built-in ProcessStream address, opt-in elsewhere with .Streaming(). A collection body (List<T>) stays one message, as it is for every other transport. A stream body on a unary address is a hard error naming the address — a unary call carries exactly one message, so silently sending the enumerable's type name would be worse.

The producer streams too: .Streaming() on a client endpoint makes the call server-streaming and puts an IAsyncEnumerable into Out.Body, so a gRPC stream flows straight into a streaming consumer (the HTTP one turns it into SSE or chunked output) without being buffered in between.

Fluent Builder API

Category Methods
Address GrpcDsl.Listen(), GrpcDsl.Call(), .Method(), .Service(service, method), .Host(), .Port()
Server .InOut(), .Streaming(), .Health(), .MaxRequestMessageSize(), .EmitHttpCompatHeaders(), .AllowClientReservedHeaders(), .SuppressStatusMapping()
Client .Plaintext(), .Deadline(), .ThrowOnError(), .MaxSendMessageSize(), .MaxReceiveMessageSize(), .ClientCertificate()
Security .Ssl(), .SslCertPath(), .SslCertPassword(), .ClientCertificates(), .ConnectionFactory(), .NegotiationType()
Sizes .MaxMessageSize()

Headers

Header Direction Meaning
redbGrpc.Route / .Service / .Method in method address the call arrived on, split
redbGrpc.RemoteIp / .RemotePort in client address, bare
redbGrpc.RemotePeer in gRPC-style peer (ipv4:10.0.0.5:51234)
redbGrpc.Authority / .Deadline / .Port in call metadata
redbGrpc.ClientCert* in client certificate, when mTLS is on
redbGrpc.StatusCode / .StatusDetail out status returned to the caller
redbGrpc.Trailer.* out response trailers

Inbound headers carrying a transport-reserved prefix (redbHttp., redbGrpc., redbSoap., …) are dropped: a caller must not be able to forge the metadata upstream processors trust, such as the client IP that drives rate limiting. allowClientReservedHeaders=true opts out, and says so in the log.

Apache Camel parity

camel-grpc redb.Route.Grpc
grpc://host:port/service?method= same, plus the full-address form grpc:host:port/pkg.Service/Method
maxMessageSize maxMessageSize (or per-direction maxSend/maxReceive/maxRequestMessageSize)
negotiationType=PLAINTEXT\|TLS negotiationType, or plaintext / ssl
keyCertChainResource, keyResource, trustCertCollectionResource sslCertPath + sslCertPassword (PFX — the .NET idiom), clientCertificateMode, allowedClientThumbprints
producerStrategy=SIMPLE / STREAMING unary by default, .Streaming() for a server-streaming call
consumerStrategy=AGGREGATION\|PROPAGATION n/a — the consumer serves unary and server-streaming; client-streaming is not implemented
forwardOnCompleted, forwardOnError n/a — same reason
flowControlWindow, maxConcurrentCallsPerConnection not exposed: they are Kestrel listener limits on the shared host
gzip compression .Compression(GrpcCompression.Gzip); inbound gzip always accepted
gRPC server reflection not served — reflection needs the descriptor set of the .proto the routes speak, which belongs to the module that owns it

Interop

Because the wire protocol is ours, the connector is verified against an independent stack: a Node.js @grpc/grpc-js server and client in a container (C:\Work\yaml\grpc), both directions, cross-process, covering statuses, server streaming, gzip and a real mTLS handshake with a client certificate. The contract there is a typed .proto, so it also proves a generated client can call a redb route with no server stubs on our side.

cd C:\Work\yaml\grpc && docker compose up -d
dotnet test tests/redb.Route.Tests.Grpc --filter Category=Interop

The tests are gated on the container being reachable, so the normal suite stays green without Docker.

Part of

redb.Route — ESB & EIP Framework for .NET

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on redb.Route.Grpc:

Package Downloads
redb.Identity.Grpc

gRPC facade for redb.Identity — service-to-service surface: token, introspection, revocation, userinfo, discovery, JWKS.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.7.0 16 8/25/2026
3.6.0 87 8/13/2026
3.5.1 83 8/9/2026
3.5.0 92 8/6/2026
3.4.0 96 7/27/2026
3.3.3 110 7/16/2026
3.3.1 109 7/10/2026
3.3.0 107 7/8/2026
3.2.0 106 6/29/2026
3.1.0 113 6/6/2026
3.0.1 106 6/3/2026
3.0.0 108 5/29/2026
2.0.2 129 5/16/2026
2.0.0 70 5/6/2026