SphereRabbitMQ is a .NET 10 RabbitMQ toolkit split into two clearly separated parts:
SphereRabbitMQ.IaC: infrastructure-as-code for RabbitMQ topologySphereRabbitMQ: runtime publisher/subscriber library for applications
The separation is strict by design:
- the IaC tool owns topology
- the runtime library never creates or mutates topology
This repository is intentionally opinionated around externally managed RabbitMQ infrastructure, broker-based retries, and explicit operational safety.
Infrastructure toolchain and CLI for:
- virtual hosts
- exchanges
- queues
- bindings
- dead-letter topology
- broker-based retry topology based on TTL + dead-letter reinjection
- reconciliation, export, and controlled migration
CLI documentation: docs/cli.md
Distribution:
- runtime library: NuGet package
SphereRabbitMQ - CLI: NuGet
dotnet toolpackageSphereRabbitMQ.IaC.Toolproviding thesprmqcommand
Pipeline integration:
- local GitHub Action:
.github/actions/apply-rabbitmq-topology - local GitHub Action:
.github/actions/destroy-rabbitmq-vhost - local GitHub Action:
.github/actions/purge-rabbitmq-queues - validates the YAML first and then runs
sprmq apply - supports non-interactive
destroyandpurgeexecution for CI/CD maintenance workflows - can reuse a repository
dotnet-tools.jsonmanifest or installSphereRabbitMQ.IaC.Toolas a fallback
Runtime library for:
- publishing messages to existing exchanges
- publishing with either a configured default routing key or a per-call routing-key override
- consuming from existing queues
- broker-based retry forwarding
- dead-letter forwarding
- topology validation at startup
Runtime documentation: docs/runtime-library.md
This repository now includes composite GitHub Actions under .github/actions/ so a workflow can execute sprmq operations without duplicating the bootstrap steps in every pipeline.
Available actions:
.github/actions/apply-rabbitmq-topology.github/actions/destroy-rabbitmq-vhost.github/actions/purge-rabbitmq-queues
Shared inputs:
topology_file: relative or absolute path to the topology YAML filedotnet_root: directory wheredotnetis already installedsprmq_tool_path: optional writable directory for fallback tool installationtool_version: optionalSphereRabbitMQ.IaC.Toolversion used by the fallback install path
Additional destructive-action input:
dry_run: optional flag fordestroyandpurge
Broker settings should be provided through the same environment variables used by the CLI:
SPHERE_RABBITMQ_MANAGEMENT_URLSPHERE_RABBITMQ_USERNAMESPHERE_RABBITMQ_PASSWORDSPHERE_RABBITMQ_VHOSTSwhen the workflow needs to restrict the virtual hosts in scope
Example workflow step:
jobs:
apply-topology:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x
- name: Apply RabbitMQ topology
uses: ./.github/actions/apply-rabbitmq-topology
with:
topology_file: samples/minimal-topology.yaml
dotnet_root: ${{ env.DOTNET_ROOT }}
env:
SPHERE_RABBITMQ_MANAGEMENT_URL: ${{ secrets.SPHERE_RABBITMQ_MANAGEMENT_URL }}
SPHERE_RABBITMQ_USERNAME: ${{ secrets.SPHERE_RABBITMQ_USERNAME }}
SPHERE_RABBITMQ_PASSWORD: ${{ secrets.SPHERE_RABBITMQ_PASSWORD }}For CLI-specific details, see docs/cli.md.
For delayed delivery patterns, the YAML topology can declare a queue whose expired messages are dead-lettered directly into another queue.
This is expressed explicitly as dead-lettering, not as a generic queue-to-queue binding.
Example:
virtualHosts:
- name: sales
queues:
- name: orders.delay.5m
ttl: "00:05:00"
deadLetter:
enabled: true
destinationType: queue
queueName: orders.consume
- name: orders.consumeBehavior:
orders.delay.5mkeeps the message for00:05:00- once the TTL expires, RabbitMQ dead-letters the message through the default exchange
- the message is routed into
orders.consume
Notes:
destinationType: queuerequiresqueueName- this does not generate a dedicated DLX/DLQ pair
- RabbitMQ still implements it internally through dead-lettering to the default exchange, not through a native queue-to-queue binding primitive
Applications that do not want to apply topology only through CI/CD can also load and apply the topology YAML during host startup.
This capability is provided by the IaC runtime integration package and runs before subscriber startup.
Example:
using SphereRabbitMQ.IaC.Infrastructure.RabbitMQ.DependencyInjection;
services.AddSphereRabbitMqWithTopologyInitialization(
configureRabbitMq: options =>
{
options.SetConnectionString("amqp://user:pass@rabbitmq:5672/sales");
},
configureTopologyInitialization: options =>
{
options.YamlFilePath = "rabbitmq/topology.yaml";
// options.ManagementUrl = "http://rabbitmq:15672/api/";
// options.AllowMigrations = true;
});You can also keep the existing runtime registration and add the startup initializer separately:
services.AddSphereRabbitMq(options =>
{
options.SetConnectionString("amqp://user:pass@rabbitmq:5672/sales");
});
services.AddSphereRabbitMqTopologyInitialization(options =>
{
options.YamlFilePath = "rabbitmq/topology.yaml";
});Operational rules:
- the YAML file is parsed, normalized, validated, and planned before apply
- if the plan contains destructive or unsupported changes, startup fails unless
AllowMigrations = true - topology initialization runs before runtime topology validation and before subscribers begin consuming
- this is intended for teams that want self-contained application startup instead of a separate topology deployment pipeline
The runtime library must never declare or modify RabbitMQ topology.
That means it will not create:
- virtual hosts
- exchanges
- queues
- bindings
If any expected topology is missing, the runtime fails explicitly with a diagnostic error.
src/SphereRabbitMQ.Abstractionssrc/SphereRabbitMQ.Domainsrc/SphereRabbitMQ.Applicationsrc/SphereRabbitMQ.Infrastructure.RabbitMQsrc/SphereRabbitMQ.DependencyInjectionsrc/SphereRabbitMQ.IaC.Domainsrc/SphereRabbitMQ.IaC.Applicationsrc/SphereRabbitMQ.IaC.Infrastructure.RabbitMQsrc/SphereRabbitMQ.IaC.Infrastructure.Yamlsrc/SphereRabbitMQ.IaC.Clitests/SphereRabbitMQ.Tests.Unittests/SphereRabbitMQ.Tests.Integrationtests/SphereRabbitMQ.IaC.Tests.Unittests/SphereRabbitMQ.IaC.Tests.Integration
- Install or restore
SphereRabbitMQ.IaC.Tool. - Scaffold a starting YAML with
sprmq initor reusesamples/templates/. - Define or adapt the topology in YAML.
- Apply it with
sprmq. - Optionally bootstrap from a live broker with
sprmq export --include-broker. - Enable shell completions with
sprmq completion <bash|zsh|pwsh>. - Start application code with
SphereRabbitMQ. - Publish and consume only against pre-existing broker resources.
AddSphereRabbitMq() can resolve runtime connection settings from environment variables even when the configuration delegate is omitted.
Recognized variables:
SPHERE_RABBITMQ_CONNECTION_STRINGSPHERE_RABBITMQ_AMQP_HOSTSPHERE_RABBITMQ_AMQP_PORTSPHERE_RABBITMQ_AMQP_VHOSTSPHERE_RABBITMQ_USERNAMESPHERE_RABBITMQ_PASSWORDSPHERE_RABBITMQ_MANAGEMENT_URLas a host fallback whenSPHERE_RABBITMQ_AMQP_HOSTis not set
Precedence is:
- explicit
AddSphereRabbitMq(options => ...)configuration SPHERE_RABBITMQ_CONNECTION_STRING- granular
SPHERE_RABBITMQ_AMQP_*plus credentials - built-in defaults (
localhost:5672,guest/guest,/)
Example without an explicit delegate:
services.AddSphereRabbitMq();Subscriber handlers may fail in three distinct ways:
- retryable failure: the message is forwarded to retry topology
- non-retryable failure: the message skips retries and goes to DLQ if configured
- discard failure: the message is acked and dropped intentionally
Built-in exception semantics:
NonRetriableMessageException: never retriedDiscardMessageException: never retried and never dead-lettered
Additional non-retryable exception types can be configured per subscriber through settings.
The IaC CLI supports apply --migrate for broker resources that RabbitMQ cannot redeclare in place.
Migration rules are explicit and tested:
- incompatible exchanges are recreated and bindings are restored from YAML
- generated queues are recreated directly
- mainstream queues are migrated through a temporary queue
- concurrent migrations are serialized with the lock queue
sprmq.migration.lock
Runtime:
dotnet test tests/SphereRabbitMQ.Tests.Unit/SphereRabbitMQ.Tests.Unit.csprojdotnet test tests/SphereRabbitMQ.Tests.Integration/SphereRabbitMQ.Tests.Integration.csproj
IaC:
dotnet test tests/SphereRabbitMQ.IaC.Tests.Unit/SphereRabbitMQ.IaC.Tests.Unit.csprojdotnet test tests/SphereRabbitMQ.IaC.Tests.Integration/SphereRabbitMQ.IaC.Tests.Integration.csproj