Skip to content

gRPC bridge

Status: target state, not implemented. The mapping is defined in ACTOR-COMMUNICATION-CONTRACT.md. No gRPC bridge actor exists in the codebase today. This page documents the target so that when the bridge is built, the contract is already settled.

The mapping

Source: WORKSTREAMS/core-and-packaging/ACTOR-COMMUNICATION-CONTRACT.md § "How they map to protocol gateways".

Matrix declarationgRPC primitive
static acceptsUnary RPCs (request → response)
static emitsServer streaming (request → stream of responses)
static streamsServer streaming (durable; replayable from JetStream)
static stateUnary RPCs (get state → value)
static blackboardBidirectional streaming (read + watch updates)
static subscribes(informational; not exposed)

Why gRPC fits

gRPC is the closest match to Matrix's request/reply + pub/sub model:

  • Unary RPCs map exactly to accepts ops.
  • Server streaming maps exactly to event subscriptions and durable streams.
  • Bidirectional streaming maps to blackboard read+write conversations.
  • Protocol Buffers give typed schemas with a stable wire format.
  • gRPC's deadline propagation is the analog of Matrix's request timeout.

Schema generation

accepts.<op>.schema and accepts.<op>.returns map to Protocol Buffer messages. Per-field types map naturally:

FieldDescriptorProtobuf
type: 'string'string
type: 'number'double (default) or int64 (with hint)
type: 'boolean'bool
type: 'object', properties: {...}nested message
type: 'array', items: ...repeated <items>
optional: trueoptional <type>
enum: [...]enum definition

The generated .proto file declares one service per actor:

proto
// generated for actor at COM.NIMBLETEC.RICHARD-SANTOMAURO/market.feed
service MarketFeed {
  rpc MarketQuote(MarketQuoteRequest) returns (MarketQuoteResponse);
  rpc MarketTickStream(MarketTickStreamRequest) returns (stream MarketTick);
  rpc MarketTradesStream(MarketTradesStreamRequest) returns (stream MarketTrade);
}

message MarketQuoteRequest {
  string symbol = 1;
}

message MarketQuoteResponse {
  double price = 1;
  double volume = 2;
  // ...
}

Field numbers are stable per declaration version (see Compatibility rules).

Streaming emits vs streams

Both map to gRPC server streaming, but the semantics differ:

  • emits → ephemeral. Late subscribers see only events from after they attached. The gRPC stream produces no historical data.
  • streams → durable. The stream consumer can request historical replay (startTime, lastSeq). The gRPC stream first replays the requested range, then tails live data.

A gRPC client that wants both ephemeral events and durable history calls two separate RPCs (or one streams RPC if the actor declares both emits and streams).

Authentication

Standard gRPC auth options apply:

MechanismUse case
TLS client certsservice-to-service inside an organization
Bearer token (metadata.authorization)per-call principal identity
Per-tenant API keyserver-to-server calls

The bridge derives the principal from the auth metadata and calls the target actor under that principal — same as the REST bridge.

Deadlines

gRPC's per-call deadline maps to Matrix's request timeout. The bridge:

  1. Reads the gRPC deadline from the call context.
  2. Computes timeoutMs = deadline - now.
  3. Passes timeoutMs to the actor invocation.

When the deadline expires, the bridge cancels the actor call (sending a session cancel where applicable) and returns gRPC DEADLINE_EXCEEDED.

Error mapping

Matrix error replies map to gRPC status codes:

Matrix { ok: false, error, code? }gRPC status
code: 'NOT_FOUND'NOT_FOUND
code: 'INVALID_ARGUMENT'INVALID_ARGUMENT
code: 'PERMISSION_DENIED'PERMISSION_DENIED
code: 'UNAUTHENTICATED'UNAUTHENTICATED
code: 'UNAVAILABLE'UNAVAILABLE
code: 'DEADLINE_EXCEEDED'DEADLINE_EXCEEDED
(other)INTERNAL

The error catalog convergence in Error codes makes this mapping deterministic.

What is missing today

  • No gRPC bridge actor exists. (Target package @open-matrix/bridge-grpc is filed but not implemented.)
  • The .proto generation logic does not exist.
  • No per-actor static grpc declaration (the analog of the static rest proposed in REST/OpenAPI bridge).

The building blocks are present — $introspect returns full declarations, system.catalog enumerates them — so when the bridge lands, the schema-generation side is mechanical.

See also