Orleans.Lattice.Api.Replication.Grpc architecture
This page documents Orleans.Lattice.Api.Replication.Grpc 9.9.0, in the documentation for Orleans.Lattice 9.9.0 (release line 9.9), built 2026-10-04. It is also published as markdown, with every table and list, at architecture.md, and llms.txt lists every page.This binding adapts the transport-agnostic ILatticeReplicationControl facade, and the read-only ILatticeReplicationStatus peer-status facade, onto gRPC without a hand-written .proto. It adds transport and authentication concerns and nothing else: the control semantics stay in the facade and the engine.
Code-first binding
Each facade operation is one gRPC Method<TRequest, TResponse> built from C# definitions. The request and response messages are [GenerateSerializer] records marshalled with the Orleans binary serializer, so the same serializer closure that runs in the cluster marshals the wire messages. There is no generated stub and no schema drift between a .proto and the records: the records are the contract. The public LatticeReplicationApiGrpcClient wraps a caller-supplied CallInvoker; the service that dispatches to the facade is internal.
The peer-status facade is a second, separately mapped service, orleans.lattice.api.replication.status. Its one unary RPC, GetPeerStatus, carries the facade's own ReplicationPeerStatusQuery and ReplicationPeerStatusPage records rather than separate wire records, and the public LatticeReplicationStatusGrpcClient implements ILatticeReplicationStatus over it, so a remote caller programs against the same contract as an in-process one.
Two-layer, fail-closed authorization
Two independent gates guard every operation RPC. The transport meta-authorizer defaults to deny; the facade access gate denies by default once an authorization add-on such as Orleans.Lattice.Auth supplies it, and with only the core no-op gate registered it allows every call.
- Transport meta-authorizer. The interceptor consults
ILatticeReplicationApiAuthorizerfor every guarded RPC before the service runs. WithRequireAuthorizationon and no authorizer registered,DenyAllReplicationApiAuthorizerrejects every call withPermissionDenied. The interceptor maps the inbound method to aLatticeReplicationApiOperation; an unrecognized method maps toUnknown, which the default-deny posture never grants, so a new or malformed method can never fall through to an allow. The interceptor guards the peer-status service the same way: aGetPeerStatuscall maps toLatticeReplicationApiOperation.GetPeerStatus, with the query's tree filter as its target. - Facade access gate. The identity bridge resolves the caller's credential from the configured header and stamps the ambient Lattice credential, and the service lifts any tenant the caller asserts in
ActiveTenantHeaderNameonto the ambient active-tenant scope for the call; the facade then re-authorizes that resolved caller against its own fail-closed access gate for theLatticeOperation.Replicationcapability on the target tree. This is the same gate an in-process caller passes through, so the wire path is no weaker than the local one. The peer-status service bridges the credential and the asserted tenant the same way, and the status facade then reports only the trees the resolved caller holds that capability over.
Neither gate replaces the other: the meta-authorizer decides whether a call may run at all; the access gate decides whether the resolved subject may act on the specific tree.
Discoverable sign-in
GetAuthScheme is the only unauthenticated RPC. It returns the host-configured AdvertisedAuthSchemes so a client can discover how to authenticate before it holds a credential. Each advertised descriptor carries only public configuration; a secret is never advertised.
Status mapping
The service maps facade outcomes to stable gRPC status codes so a client sees a predictable contract:
| Facade outcome | Status |
|---|---|
LatticeAuthorizationDeniedException (the facade access gate) |
PermissionDenied |
LatticeTenantAccessDeniedException (an asserted tenant the caller may not act as, or a sys- / malformed t/ tree name under one) |
PermissionDenied |
LatticeReplicationPreconditionFailedException |
FailedPrecondition |
LatticeReplicationModeChangeRejectedException |
FailedPrecondition |
ArgumentException (for example a null / empty tree id) |
InvalidArgument |
OperationCanceledException |
Cancelled |
| Any other exception | Internal (logged server-side; the client sees a non-leaking message) |
GetPeerStatus maps the same way, without the two precondition outcomes: a negative page size or a malformed continuation token is an ArgumentException, so it surfaces as InvalidArgument.
An already-thrown RpcException is rethrown unchanged so an inner status is preserved. A transport meta-authorizer denial never reaches the service: the interceptor raises it directly as PermissionDenied (and a cancelled authorization check as Cancelled).
See also
Orleans.Lattice.Api.Replication- the facade's authorization and engine-delegation model.- runtime replication configuration - the engine-side config tree and fail-closed resolution.
Orleans.Lattice.Api.Backup.Grpc- the sibling gRPC binding this one mirrors.