Stella Router ASP.NET WebService Integration Guide
This guide explains how to integrate any ASP.NET Core WebService with the Stella Router for automatic endpoint discovery and dispatch.
Prerequisites
Add a project reference to StellaOps.Router.AspNet:
<ProjectReference Include="../../__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
Integration Steps
1. Add Router Options to Service Options
In your service’s options class (e.g., MyServiceOptions.cs), add:
using StellaOps.Router.AspNet;
public class MyServiceOptions
{
// ... existing options ...
/// <summary>
/// Stella Router integration configuration (disabled by default).
/// </summary>
public StellaRouterOptionsBase? Router { get; set; }
}
2. Register Services in Program.cs
Add the using directive:
using StellaOps.Router.AspNet;
After service registration (e.g., after AddControllers()), add:
// Stella Router integration
builder.Services.TryAddStellaRouter(
serviceName: "my-service-name",
version: typeof(Program).Assembly.GetName().Version?.ToString() ?? "1.0.0",
routerOptions: options.Router);
Optional: generic microservice transport registration
For services that should auto-register transport clients from configuration, use:
builder.Services.AddRouterMicroservice(
builder.Configuration,
serviceName: "my-service-name",
version: typeof(Program).Assembly.GetName().Version?.ToString() ?? "1.0.0",
routerOptionsSection: "MyService:Router");
AddRouterMicroservice(...) keeps TryAddStellaRouter(...) behavior and registers transport clients through RouterTransportPluginLoader based on configured gateway transport types (InMemory, Tcp, Certificate/tls, Udp, RabbitMq, Messaging).
The StellaOps.Router.AspNet library does not hard-reference transport assemblies; transports are activated from plugin DLLs and environment/config values.
For Valkey messaging mode, configure:
myservice:
router:
enabled: true
region: "local"
transportPlugins:
directory: "plugins/router/transports"
searchPattern: "StellaOps.Router.Transport.*.dll"
gateways:
- host: "router.stella-ops.local"
port: 9100
transportType: "Messaging"
messaging:
transport: "valkey"
pluginDirectory: "plugins/messaging"
searchPattern: "StellaOps.Messaging.Transport.*.dll"
requestQueueTemplate: "router:requests:{service}"
responseQueueName: "router:responses"
consumerGroup: "myservice"
requestTimeout: "30s"
leaseDuration: "5m"
batchSize: 10
heartbeatInterval: "10s"
valkey:
connectionString: "cache.stella-ops.local:6379"
2.2 Gateway trust mode and identity envelope verification
Service-side Router bridge can enforce gateway-issued identity semantics:
myservice:
router:
authorizationTrustMode: "GatewayEnforced" # ServiceEnforced | Hybrid | GatewayEnforced
identityEnvelopeSigningKey: "${ROUTER_IDENTITY_SIGNING_KEY}"
identityEnvelopeClockSkewSeconds: 30
ServiceEnforced: service-local checks remain primary.Hybrid: prefer signed envelope; fallback to legacy headers.GatewayEnforced: fail closed when envelope is missing/invalid.
2.2a Your RequireAuthorization(...) is NOT a backstop on the router path
The trust mode above decides how the principal is populated. It does not make your host enforce its policies for router-dispatched requests: AspNetRouterRequestDispatcher invokes the endpoint delegate directly and your UseAuthorization() middleware never runs. The gateway enforces the claim requirements your host published, and it publishes only what DefaultAuthorizationClaimMapper can read off your policy’s requirement objects. A policy built with RequireAssertion(...) publishes nothing — the gateway then authenticates the caller and lets any token through (SPRINT_20260904_003 ROA-1, measured live 2026-09-04).
Do this:
- Resource-server (
StellaOpsBearer) hosts:options.AddStellaOpsScopePolicy(name, scope)orAddStellaOpsAnyScopePolicy(name, scopes...). If the policy already carriesRequireAuthenticatedUser(), useRequireAnyStellaOpsScopes(...)on the builder instead — theAdd…Policyshorthand replaces the whole policy and drops that rule. - Identity-envelope hosts (this guide’s
GatewayEnforcedshape): a host-local requirement type exposingRequiredScopesandRequireAllScopesplus anAuthorizationHandler<T>registered asIAuthorizationHandler— copyAdvisoryAiScopeRequirement(src/AdvisoryAI/.../Security/),GraphScopeRequirement, orAirGapScopeRequirement. Do not useAddStellaOpsScopePolicyhere; it binds to a scheme your host does not register. - Ask the question per BRANCH, not per host. If your
AddAuthorizationblock runs in every authentication mode while the scheme registration does not — a Testing/development handler beside a production resource server, say — the host-local shape is the only one both branches can satisfy. Register the policies and their handler together in one method so a test-composed host cannot take one without the other. - Verify, do not assume: a direct probe against your host proves only the host’s own middleware. Read the registration, or probe through the gateway. Startup logs carry a Warning naming any policy the mapper could not publish, and
GatewayUnmappablePolicyConformanceTestsfails the build on a newRequireAssertion. Inventory and register:docs/architecture/gateway-authorization/. - There is no
Router__OnMissingAuthorizationenvironment key, and looking for one is the trap this bullet exists to stop.StellaRouterOptions.OnMissingAuthorizationis a CODE-level option (AddStellaRouter(opts => ...));StellaRouterOptionsBase, which is whatAddRouterMicroservicebinds theRouter:section into, has no such property, so an environment key of that name was read by nothing. It was set on every router-enabled service until 2026-09-07 and was deleted from compose, the env examples and the release bundle in the same change;Compose_NeverSetsTheUnbound- OnMissingAuthorizationKeyfails if it comes back, andRouterOnMissingAuthorization_IsNotBoundFromConfiguration_PinnedUntilWiredfails if the property is wired to configuration without dealing with the deployed values. Hosts therefore run theStellaRouterOptionsdefaultWarnAndAllow: an endpoint with no authorization metadata is published authenticated-only with a warning. That is a SEPARATE layer from the fail-closed gateway rule below — those endpoints declare no policy, so the gateway does not refuse them.
2.3 Timeout precedence
Gateway dispatch timeout is now resolved with explicit precedence:
- Endpoint timeout (including endpoint override/service default published by service).
- Route default timeout (optional per gateway route via
defaultTimeout). - Gateway routing default timeout (
Gateway:Routing:DefaultTimeout). - Global gateway cap (
Gateway:Routing:GlobalTimeoutCap).
Route-level timeout example:
gateway:
routing:
defaultTimeout: "30s"
globalTimeoutCap: "120s"
routes:
- type: Microservice
path: "/api/v1/timeline"
translatesTo: "http://timelineindexer.stella-ops.local/api/v1/timeline"
defaultTimeout: "15s"
2.1 Gateway SPA deep-link handling with microservice routes
When gateway route prefixes overlap with UI routes (for example /policy), browser navigations must still resolve to the SPA shell.
Gateway RouteDispatchMiddleware now serves the configured static SPA fallback route for browser document requests on both ReverseProxy and Microservice route types. API prefixes (/api, /v1) are explicitly excluded from this fallback and continue to dispatch to backend services.
3. Enable Middleware
After UseAuthorization(), add:
app.TryUseStellaRouter(resolvedOptions.Router);
4. Refresh Endpoint Cache
After all endpoints are mapped (before app.RunAsync()), add:
app.TryRefreshStellaRouterEndpoints(resolvedOptions.Router);
Configuration Example (YAML)
myservice:
router:
enabled: true
region: "us-east-1"
defaultTimeoutSeconds: 30
heartbeatIntervalSeconds: 10
gateways:
- host: "router.stellaops.local"
port: 9100
transportType: "Tcp"
useTls: true
certificatePath: "/etc/certs/router.pem"
WebServices Integration Status
All WebServices have been updated with Router integration:
| Service | Path | Status |
|---|---|---|
| Scanner.WebService | src/Scanner/StellaOps.Scanner.WebService | ✅ Complete |
src/__Obsoleted/Concelier/StellaOps.Concelier.WebService | Retired 2026-09-11 (VULN-G6) | |
| Excititor.WebService | src/Excititor/StellaOps.Excititor.WebService | ✅ Complete |
| Gateway.WebService | src/Router/StellaOps.Gateway.WebService (moved from src/Gateway/, Sprint 200) | ✅ Complete |
| VexHub.WebService | src/VexHub/StellaOps.VexHub.WebService | ✅ Complete |
| Attestor.WebService | src/Attestor/StellaOps.Attestor/StellaOps.Attestor.WebService | ✅ Complete |
| EvidenceLocker.WebService | src/EvidenceLocker/StellaOps.EvidenceLocker/StellaOps.EvidenceLocker.WebService | ✅ Complete |
| Findings.Ledger.WebService | src/Findings/StellaOps.Findings.Ledger.WebService | ✅ Complete |
| AdvisoryAI.WebService | src/AdvisoryAI/StellaOps.AdvisoryAI.WebService | ✅ Complete |
| IssuerDirectory.WebService | src/IssuerDirectory/StellaOps.IssuerDirectory/StellaOps.IssuerDirectory.WebService | ✅ Complete |
src/__Obsoleted/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService | n/a — surface served by notify-web | |
| Notify.WebService | src/Notify/StellaOps.Notify.WebService | ✅ Complete |
| PacksRegistry.WebService | src/PacksRegistry/StellaOps.PacksRegistry/StellaOps.PacksRegistry.WebService | ✅ Complete |
| — deleted 2026-08-27 (SPRINT_20260722_010 FND-10) | n/a — the host is retired; the Findings family integrates as findings-web | |
| Signer.WebService | src/Signer/StellaOps.Signer/StellaOps.Signer.WebService | ✅ Complete |
| TaskRunner.WebService | src/TaskRunner/StellaOps.TaskRunner/StellaOps.TaskRunner.WebService | ✅ Complete |
| TimelineIndexer.WebService | archived 2026-08-10 → src/__Obsoleted/Timeline/StellaOps.TimelineIndexer.WebService | ✅ Complete (surface now served by timeline-web) |
| Orchestrator.WebService | src/JobEngine/StellaOps.JobEngine/StellaOps.JobEngine.WebService | ✅ Complete |
| Scheduler.WebService | src/Scheduler/StellaOps.Scheduler.WebService | ✅ Complete |
| ExportCenter.WebService | src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.WebService | ✅ Complete |
Files Created
The Router.AspNet library includes the following files:
StellaOps.Router.AspNet.csproj- Project fileStellaRouterOptions.cs- Unified router optionsStellaRouterExtensions.cs- DI extensions (AddStellaRouter,UseStellaRouter)CompositeRequestDispatcher.cs- Routes requests to ASP.NET or Stella endpointsStellaRouterOptionsBase.cs- Base options class for embedding in service optionsStellaRouterIntegrationHelper.cs- Helper methods for conditional integration
