Router Transport Plugins

StellaOps Router uses a plugin-based transport architecture that enables runtime loading of transport implementations. This allows operators to deploy only the transports they need and swap implementations without recompiling the Gateway.

Available Transports

TransportPlugin AssemblyUse CaseStatus
TCPStellaOps.Router.Transport.Tcp.dllInternal services, same datacenterStable
TLSStellaOps.Router.Transport.Tls.dllCross-datacenter, mTLSStable
UDPStellaOps.Router.Transport.Udp.dllFire-and-forget, broadcastStable
RabbitMQStellaOps.Router.Transport.RabbitMq.dllAsync processing, fan-outStable
InMemoryStellaOps.Router.Transport.InMemory.dllDevelopment, testingStable
ValkeyStellaOps.Messaging.Transport.Valkey.dllDistributed, pub/subStable
PostgreSQLStellaOps.Messaging.Transport.Postgres.dllTransactional, LISTEN/NOTIFYStable

Plugin Architecture

Loading Model

Transport plugins are loaded at Gateway startup via RouterTransportPluginLoader:


The gateway host must not depend on concrete optional transport server classes.
Transport implementations expose gateway-owned contracts from
`StellaOps.Router.Common`: connection-oriented transports register
`IGatewayConnectionTransportServer`, while queue-backed messaging registers
`IGatewayMessagingTransportServer`. The host dispatch path consumes those
contracts so TCP, TLS, RabbitMQ, UDP, InMemory, and non-default messaging
backends can move to signed mounted bundles without changing gateway service
code. The gateway runtime project no longer references concrete Router
transport or Messaging backend implementations; the default Messaging + Valkey
path must be supplied by mounted bundles.
┌─────────────────────────────────────────────────────────────────┐
│                     Gateway Startup                              │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │ RouterTransportPluginLoader                                │ │
│  │                                                             │ │
│  │  1. Scan plugins/router/transports/                        │ │
│  │  2. Load assemblies in isolation (AssemblyLoadContext)     │ │
│  │  3. Discover IRouterTransportPlugin implementations        │ │
│  │  4. Register configured transport with DI                  │ │
│  └────────────────────────────────────────────────────────────┘ │
│                           │                                      │
│                           ▼                                      │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │ Transport Plugin (e.g., TLS)                               │ │
│  │                                                             │ │
│  │  - TransportName: "tls"                                    │ │
│  │  - DisplayName: "TLS Transport"                            │ │
│  │  - IsAvailable(): Check dependencies                       │ │
│  │  - Register(): Wire up ITransportServer/ITransportClient   │ │
│  └────────────────────────────────────────────────────────────┘ │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Directory Structure

Mounted bundle roots use profile/plugin-id directories and are mounted read-only into the gateway container:

devops/plugins/
|-- router/
|   `-- <profile>/
|       `-- <plugin-id>/
|           |-- StellaOps.Router.Transport.<Name>.dll
|           |-- StellaOps.Router.Transport.<Name>.dll.sig
|           |-- transports.signed.json
|           `-- checksums.sha256
`-- messaging/
    `-- <profile>/
        `-- <plugin-id>/
            |-- StellaOps.Messaging.Transport.<Name>.dll
            |-- StellaOps.Messaging.Transport.<Name>.dll.sig
            |-- manifest.json
            `-- checksums.sha256

Compose mounts these roots under /app/plugins/router/... and /app/plugins/messaging/...; trust roots mount under /app/trust-roots/plugins/{router,messaging}.

The runtime packager produces the default Messaging/Valkey bundles for the base profile, Router TLS plus Messaging PostgreSQL for the recommended profile, and Router InMemory plus Messaging InMemory for the harness profile. TCP, RabbitMQ, UDP, and other non-default transports remain future profile work until their compose dependencies and admission tests are added.

Router transport bundles use the signed Router transport manifest admitted by RouterTransportPluginLoader. Messaging backend bundles use the same profile/plugin-id shape and are admitted by MessagingPluginLoader with the configured trust root, profile, and signature-enforcement setting. Gateway startup wires those values from Gateway:MessagingBackendPlugins and mirrors them into Gateway:Transports:Messaging; non-development startup forces signature verification on.

The older flat layout below is retained only for legacy image-staged mode and local migration context; mounted runtime mode uses the profile/plugin-id layout above.

plugins/
└── router/
    └── transports/
        ├── StellaOps.Router.Transport.Tcp.dll
        ├── StellaOps.Router.Transport.Tls.dll
        ├── StellaOps.Router.Transport.Udp.dll
        ├── StellaOps.Router.Transport.RabbitMq.dll
        └── StellaOps.Router.Transport.InMemory.dll

Configuration

Transport selection and options are configured in router.yaml or environment variables:

Router:
  Transport:
    Type: tls                     # Which transport plugin to use
    Tls:                          # Transport-specific options
      Port: 5101
      CertificatePath: /certs/server.pfx
      RequireClientCertificate: true
    Tcp:
      Port: 5100
      MaxConnections: 1000

Environment override:

ROUTER__TRANSPORT__TYPE=tcp
ROUTER__TRANSPORT__TCP__PORT=5100

Using Plugins in Gateway

Programmatic Loading

using StellaOps.Router.Common.Plugins;

var builder = WebApplication.CreateBuilder(args);

// Load transport plugins from signed profile/plugin-id bundle directories.
var pluginLoader = new RouterTransportPluginLoader(
    builder.Services.BuildServiceProvider().GetService<ILogger<RouterTransportPluginLoader>>());

var profileRoot = "/app/plugins/router/base";
foreach (var bundlePath in TransportPluginDirectoryProbe.GetSignedManifestLoadCandidateDirectories(
    profileRoot,
    "StellaOps.Router.Transport.*.dll"))
{
    var report = await pluginLoader.LoadFromSignedManifestDirectoryAsync(
        bundlePath,
        validator,
        trustRoot,
        transportPolicy,
        cancellationToken);

    if (!report.IsAccepted)
    {
        throw new InvalidOperationException(report.RejectionReason);
    }
}

// Register the configured transport from the admitted signed bundle.
var plugin = pluginLoader.GetPlugin("messaging");
plugin?.Register(new RouterTransportRegistrationContext(
    builder.Services,
    builder.Configuration,
    RouterTransportMode.Server));

var app = builder.Build();
// ...

Gateway Integration

The Gateway automatically loads transport plugins during startup. Configure in router.yaml:

gateway:
  name: api-gateway
  plugins:
    transports:
      directory: plugins/router/transports
      searchPattern: "StellaOps.Router.Transport.*.dll"

Creating Custom Transports

See the Transport Plugin Development Guide for creating custom transport implementations.

Minimal Plugin Example

using StellaOps.Router.Common.Plugins;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace MyCompany.Router.Transport.Custom;

public sealed class CustomTransportPlugin : IRouterTransportPlugin
{
    public string TransportName => "custom";
    public string DisplayName => "Custom Transport";

    public bool IsAvailable(IServiceProvider services) => true;

    public void Register(RouterTransportRegistrationContext context)
    {
        var configSection = context.Configuration.GetSection("Router:Transport:Custom");

        context.Services.Configure<CustomTransportOptions>(options =>
        {
            configSection.Bind(options);
        });

        if (context.Mode.HasFlag(RouterTransportMode.Server))
        {
            context.Services.AddSingleton<CustomTransportServer>();
            context.Services.AddSingleton<ITransportServer>(sp =>
                sp.GetRequiredService<CustomTransportServer>());
        }

        if (context.Mode.HasFlag(RouterTransportMode.Client))
        {
            context.Services.AddSingleton<CustomTransportClient>();
            context.Services.AddSingleton<ITransportClient>(sp =>
                sp.GetRequiredService<CustomTransportClient>());
            context.Services.AddSingleton<IMicroserviceTransport>(sp =>
                sp.GetRequiredService<CustomTransportClient>());
        }
    }
}

Transport Selection Guide

ScenarioRecommended TransportConfiguration
Development/TestingInMemoryType: inmemory
Same-datacenterTCPType: tcp
Cross-datacenter secureTLSType: tls with mTLS
High-volume asyncRabbitMQType: rabbitmq
Broadcast/fire-and-forgetUDPType: udp
Distributed with replayValkeyVia Messaging plugins
Transactional messagingPostgreSQLVia Messaging plugins

Air-Gap Deployment

For offline/air-gapped deployments:

  1. Pre-package transport plugins with your deployment
  2. Configure the plugin directory path
  3. No external network access required
gateway:
  plugins:
    transports:
      directory: /opt/stellaops/plugins/router/transports

See Also