Router
Internal communication fabric for Stella Ops — microservices talk through a central gateway over efficient binary transports.
The Stella Ops Router is the internal communication infrastructure that lets microservices communicate through a central gateway using efficient binary protocols, with health-aware routing, claims-based authorization, and centralized admission control.
Why Another Gateway?
Stella Ops already has HTTP-based services. The Router exists because:
- Performance: Binary framing eliminates HTTP overhead for internal traffic
- Streaming: First-class support for large payloads (SBOMs, scan results, evidence bundles)
- Cancellation: Request abortion propagates across service boundaries
- Health-aware Routing: Automatic failover based on heartbeat and latency
- Claims-based Auth: Unified authorization via Authority integration
- Transport Flexibility: UDP for small payloads, TCP/TLS for streams, RabbitMQ for queuing
- Centralized Rate Limiting: Admission control at the gateway (429 + Retry-After; instance + environment scopes)
The Router generalizes the earlier HTTP-to-RabbitMQ relay pattern into a simpler, transport-agnostic design.
Architecture Overview
┌─────────────────────────────────┐
│ StellaOps.Gateway.WebService│
HTTP Clients ────────────────────► (HTTP ingress) │
│ │
│ ┌─────────────────────────────┐│
│ │ Endpoint Resolution ││
│ │ Authorization (Claims) ││
│ │ Routing Decision ││
│ │ Transport Dispatch ││
│ └─────────────────────────────┘│
└──────────────┬──────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Billing │ │ Inventory │ │ Scanner │
│ Microservice │ │ Microservice │ │ Microservice │
│ │ │ │ │ │
│ TCP/TLS │ │ InMemory │ │ RabbitMQ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Components
| Component | Project | Purpose |
|---|---|---|
| Gateway | StellaOps.Gateway.WebService | HTTP ingress, routing, authorization |
| Microservice SDK | StellaOps.Microservice | SDK for building microservices |
| Source Generator | StellaOps.Microservice.SourceGen | Compile-time endpoint discovery |
| Common | StellaOps.Router.Common | Shared types, frames, interfaces |
| Config | StellaOps.Router.Config | Configuration models, YAML binding |
| Gateway library | StellaOps.Router.Gateway | Gateway DI/composition (AddRouterGateway, UseRouterGateway), routing state, weight overrides |
| ASP.NET bridge | StellaOps.Router.AspNet | Maps ASP.NET endpoint registrations as Router endpoints |
| Microservice AspNetCore bridge | StellaOps.Microservice.AspNetCore | Hosts a Router microservice inside an ASP.NET Core app |
| InMemory Transport | StellaOps.Router.Transport.InMemory | Testing transport |
| TCP Transport | StellaOps.Router.Transport.Tcp | Production TCP transport |
| TLS Transport | StellaOps.Router.Transport.Tls | Encrypted/certificate-based TCP transport (TransportType.Certificate) |
| UDP Transport | StellaOps.Router.Transport.Udp | Small payload transport |
| RabbitMQ Transport | StellaOps.Router.Transport.RabbitMq | Message queue transport |
| Messaging Transport | StellaOps.Router.Transport.Messaging | Messaging/RPC transport over StellaOps.Messaging (Valkey/Postgres/InMemory backends) |
| Unified transport plugin | StellaOps.Router.Plugin.Unified | Plugin adapter that exposes Router transports as loadable plugins |
Transport selection is driven by the StellaOps.Router.Common.Enums.TransportType enum, whose values are: InMemory, Udp, Tcp, Certificate (TLS/mTLS), RabbitMq, and Messaging. HTTP is explicitly excluded by design (see Invariants).
Gateway Control-Plane Endpoints
Beyond proxying microservice routes, the gateway (StellaOps.Gateway.WebService) hosts its own HTTP control-plane and system endpoints. These are treated as “system paths” so they bypass RouteDispatchMiddleware and resolve directly. Scope constants are declared in the gateway endpoint classes and registered in the canonical scope catalog (StellaOps.Auth.Abstractions/StellaOpsScopes.cs).
| Method | Path | Auth | Source |
|---|---|---|---|
| GET | /api/v1/router/instances (?service= filter) | scope router:routing:read (policy router.routing.read) | RouterInstancesEndpoints |
| GET | /api/v1/router/routing/weights | scope router:routing:read | RoutingWeightEndpoints |
| POST | /api/v1/router/routing/weights | scope router:routing:write (policy router.routing.write) | RoutingWeightEndpoints |
| POST | /api/v1/gateway/administration/router/resync | scope router:routing:write (policy router.routing.write) | RouterAdministrationEndpoints / GatewayRegistrationResyncService |
| GET | /buildinfo.json | anonymous (by design) | BuildInfoAggregatorEndpoints |
| GET | /api/openapi/aggregate | anonymous | OpenApiAggregatorEndpoints |
| GET | /openapi.json, /openapi.yaml, /.well-known/openapi | anonymous | OpenApiAggregatorEndpoints |
| GET | /api/v1/operator/verify/buildinfo | anonymous | VerifyEndpoints |
| GET | /api/v1/operator/verify/sbom-by-digest/{sha256} | scope operator.verify.read | VerifyEndpoints |
| GET | /api/v1/operator/verify/ledger-chain/{ledgerId} | scope operator.verify.read | VerifyEndpoints |
| GET | /api/v1/operator/verify/token-revocation | scope operator.verify.read | VerifyEndpoints |
| GET | /api/v1/operator/verify/cra-provenance/{tenantId} | scope operator.verify.read | VerifyEndpoints |
| POST | /api/v1/operator/verify/replay-compare | scope operator.verify.admin | VerifyEndpoints |
| GET | /health, /health/live, /health/ready, /health/startup | system | HealthCheckMiddleware |
| GET | /metrics | system | (Prometheus scrape) |
Notes:
operator.verify.*scopes use dotted form (operator.verify.read,operator.verify.admin); the router routing scopes use colon form (router:routing:read,router:routing:write). Both are reflected verbatim in the source and the scope catalog.- The
POST /routing/weightsoverride is process-local (in-memoryIRoutingWeightOverrideStore), not persisted cluster-wide. - The gateway administration group is fail-secure at its route-group boundary with policy
router.routing.write. For resync, omittingconnectionIdintentionally requests all-fleet replay; a non-blank exact value targets one connection, an explicitly blank value returns400, and a stale value returns404without broadening to the fleet.
Solution Structure
The solution file is src/Router/StellaOps.Router.sln. Libraries live under src/Router/__Libraries/ and tests under src/Router/__Tests/.
src/Router/
├── StellaOps.Router.sln
├── StellaOps.Gateway.WebService/ (HTTP ingress / gateway host)
├── StellaOps.Router.Plugin.Unified/ (unified transport plugin adapter)
├── __Libraries/
│ ├── StellaOps.Router.Common/
│ ├── StellaOps.Router.Config/
│ ├── StellaOps.Router.Gateway/
│ ├── StellaOps.Router.AspNet/
│ ├── StellaOps.Router.Transport.InMemory/
│ ├── StellaOps.Router.Transport.Tcp/
│ ├── StellaOps.Router.Transport.Tls/
│ ├── StellaOps.Router.Transport.Udp/
│ ├── StellaOps.Router.Transport.RabbitMq/
│ ├── StellaOps.Router.Transport.Messaging/
│ ├── StellaOps.Microservice/
│ ├── StellaOps.Microservice.AspNetCore/
│ ├── StellaOps.Microservice.SourceGen/
│ ├── StellaOps.Messaging/
│ ├── StellaOps.Messaging.Transport.InMemory/
│ ├── StellaOps.Messaging.Transport.Postgres/
│ └── StellaOps.Messaging.Transport.Valkey/
├── __Tests/
│ └── (test projects)
├── examples/
└── plugins/
Key Documents
Module Documentation (this directory)
| Document | Purpose |
|---|---|
| architecture.md | Canonical specification and requirements |
| schema-validation.md | JSON Schema validation feature |
| openapi-aggregation.md | OpenAPI document generation |
| migration-guide.md | WebService to Microservice migration |
| rate-limiting.md | Centralized router rate limiting (dossier) |
| aspnet-endpoint-bridge.md | Using ASP.NET endpoint registration as Router endpoint registration |
| messaging-valkey-transport.md | Messaging transport over Valkey |
| timelineindexer-microservice-pilot.md | TimelineIndexer Valkey microservice transport pilot mapping and rollback |
| webservices-valkey-rollout-matrix.md | All-webservices Valkey microservice migration matrix (waves, owners, rollback) |
| microservice-transport-guardrails.md | Plugin-only transport guardrails and migration PR checklist |
| authority-gateway-enforcement-runbook.md | Operations runbook for gateway-enforced auth and signed identity envelope trust |
| IDENTITY_ENVELOPE_MIDDLEWARE.md | Signed identity-envelope middleware (HMAC-SHA256) reference |
| webservice-integration-guide.md | Integrating an existing ASP.NET WebService behind the gateway |
| rollout-acceptance-20260222.md | Dual-mode rollout acceptance package and evidence index |
Implementation Guides (docs/modules/router/guides/)
| Document | Purpose |
|---|---|
| GETTING_STARTED.md | Step-by-step setup guide |
| rate-limiting-config.md | Rate limiting configuration guide |
| rate-limiting-routes.md | Per-route rate-limiting configuration |
Quick Start
Gateway
var builder = WebApplication.CreateBuilder(args);
// Add router services
builder.Services.AddRouterGateway(builder.Configuration);
builder.Services.AddRouterGatewayLocalInMemoryHarness(builder.Environment); // local harness only
var app = builder.Build();
// Configure pipeline
app.UseRouterGateway();
await app.RunAsync();
Microservice
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddStellaMicroservice(options =>
{
options.ServiceName = "billing";
options.Version = "1.0.0";
options.Region = "us-east-1";
});
builder.Services.AddInMemoryTransportClient();
await builder.Build().RunAsync();
Endpoint Definition
[StellaEndpoint("POST", "/invoices")]
[ValidateSchema(Summary = "Create invoice")]
public sealed class CreateInvoiceEndpoint : IStellaEndpoint<CreateInvoiceRequest, CreateInvoiceResponse>
{
public Task<CreateInvoiceResponse> HandleAsync(
CreateInvoiceRequest request,
CancellationToken ct)
{
return Task.FromResult(new CreateInvoiceResponse
{
InvoiceId = Guid.NewGuid().ToString()
});
}
}
Invariants
These are non-negotiable design constraints:
- Method + Path is the endpoint identity (see
StellaEndpointAttribute(method, path)) - Strict semver for version matching
- Region from node config — the gateway region comes from
RouterNodeConfig.Region(config sectionGatewayNode, legacyRouter:Node); “it is never derived from headers or URLs” - No HTTP transport between gateway and microservices (HTTP is excluded from the
TransportTypeenum) RequiredClaims(theStellaEndpointAttribute.RequiredClaimsstring array — claims, not roles) for endpoint authorization- Opaque body handling (router doesn’t interpret payloads)
Building
# Build router solution
dotnet build src/Router/StellaOps.Router.sln
# Run tests
dotnet test src/Router/StellaOps.Router.sln
# Run gateway
dotnet run --project src/Router/StellaOps.Gateway.WebService
