Configuration Reference
Monitored Service Configuration
AddCritterWatchMonitoring()
Call this inside UseWolverine() in each service you want to monitor.
Simple form — the telemetry + control queue URIs (the service identity is the Wolverine ServiceName, not a parameter):
builder.Host.UseWolverine(opts =>
{
// CritterWatch keys this service by its Wolverine ServiceName — there is no
// separate service-name parameter on AddCritterWatchMonitoring.
opts.ServiceName = "my-service";
opts.AddCritterWatchMonitoring(
critterWatchUri: new Uri("rabbitmq://critterwatch"),
systemControlUri: new Uri("rabbitmq://my-service-control"));
});Full options form:
builder.Host.UseWolverine(opts =>
{
opts.AddCritterWatchMonitoring(
// URI of the queue CritterWatch listens on for telemetry
critterWatchUri: new Uri("rabbitmq://critterwatch"),
// URI of the queue this service listens on for incoming commands
systemControlUri: new Uri("rabbitmq://trip-service-control"),
// How this service exports metrics (default: Hybrid)
metricsMode: WolverineMetricsMode.Hybrid
);
});Options Reference
| Option | Default | Description |
|---|---|---|
RabbitMqUri | — | URI of the RabbitMQ broker (or transport endpoint) |
ServiceName | — | Unique identifier for this service in CritterWatch |
Label | ServiceName | Display name shown in the UI |
HeartbeatInterval | 1 second | How often state snapshots are published |
AgentHealthCheckInterval | 60 seconds | How often agent health is actively checked |
configureBaselines | (none) | Optional callback to declare expected throughput/execution-time baselines for this service. See Alerts › Editing Thresholds. |
Service Name Must Be Unique
The ServiceName is used as the Marten event stream key. Two services with the same name will overwrite each other's state. Use a name that uniquely identifies the service across your entire deployment.
CritterWatch Server Configuration
AddCritterWatch()
var builder = WebApplication.CreateBuilder(args);
builder.AddCritterWatch(
builder.Configuration.GetConnectionString("critterwatch")!,
opts =>
{
opts.UseRabbitMq(new Uri("amqp://localhost")).AutoProvision();
opts.ListenToRabbitQueue("critterwatch").Sequential();
});
// Single-node is the default — nothing else to configure. For a multi-node
// cluster, supply configureClusterShardedTopology (see Deployment › Clustering).
var app = builder.Build();
app.UseCritterWatch();
app.Run();UseCritterWatch()
Maps all CritterWatch middleware into the ASP.NET Core pipeline:
app.UseCritterWatch();
// With a custom SignalR route:
app.UseCritterWatch(signalRRoute: "/my-hub");UseCritterWatch() registers:
- Wolverine HTTP endpoints under
/api/critterwatch/* - SignalR hub at
/api/messages(configurable) - Static file serving for the embedded Vue SPA
- Client-side routing fallback for the SPA
Storage schema
All CritterWatch data — its documents (service summaries, alerts, metrics rollups) and its event store — is isolated to a dedicated database schema, so it never collides with the host application's own tables. The default schema is critterwatch, and the name is configurable via the schemaName parameter:
builder.AddCritterWatch(
builder.Configuration.GetConnectionString("critterwatch")!,
schemaName: "monitoring"); // default: "critterwatch"The same schemaName parameter is available on the lower-level opts.AddCritterWatchServices(...) registration (both the Marten/PostgreSQL and Polecat/SQL Server flavors) and on opts.AddCritterWatchEmbedded(...). CritterWatch only ever creates and migrates tables inside that one schema.
Why a dedicated schema matters
This is what lets CritterWatch share a database with the application it monitors without stepping on it — most important for embedded CritterWatch (CritterWatch.Embedded), where the console runs inside your app's own process and database. Point schemaName at any schema you like; CritterWatch keeps all of its storage there.
Docker Compose
A complete docker-compose.yml for local development:
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: critterwatch
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
rabbitmq:
image: rabbitmq:3-management
ports:
- "5672:5672" # AMQP
- "15672:15672" # Management UI
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
volumes:
postgres_data:Connection String Formats
PostgreSQL
Host=localhost;Port=5432;Database=critterwatch;Username=postgres;Password=postgresFor cloud providers:
Host=my-postgres.postgres.database.azure.com;Database=critterwatch;Username=app@my-postgres;Password=secret;SSL Mode=RequireRabbitMQ
amqp://guest:guest@localhost:5672/
amqps://user:pass@my-rabbit.cloud:5671/ # TLSAmazon SQS
AddCritterWatchMonitoring works identically against the SQS transport. Two queues are involved per monitored service:
| Queue | Direction | Purpose |
|---|---|---|
critterWatchUri | service → CritterWatch | Metrics, heartbeats, capability snapshots |
systemControlUri | CritterWatch → service | Pause / restart listeners, rebuild projections, DLQ ops |
opts.UseAmazonSqsTransport();
opts.AddCritterWatchMonitoring(
critterWatchUri: SqsEndpointUri.Queue("critterwatch"),
systemControlUri: SqsEndpointUri.Queue("critterwatch-control-trip-service"));Dead letter queues with AutoProvision() off
By default the WolverineFx.AmazonSqs transport attaches every listener to its DefaultDeadLetterQueueName (wolverine-dead-letter-queue). With AutoProvision() enabled the broker creates that queue on startup, so the control listener AddCritterWatchMonitoring installs is wired up cleanly.
In production environments with AutoProvision() off, that default DLQ typically isn't pre-provisioned. The broker startup then fails:
Wolverine.AmazonSqs.WolverineSqsTransportException: Error while trying to
initialize Amazon SQS queue 'wolverine-dead-letter-queue'
---> Amazon.SQS.Model.QueueDoesNotExistExceptionThree ways to handle it, pick whichever fits your infra automation:
1. Provision the default DLQ alongside your application queues (CDK / Terraform / etc.). Nothing to change in code.
2. Point the control listener at an existing DLQ you already provision. Re-open the same endpoint after AddCritterWatchMonitoring and chain DeadLetterQueueName:
opts.AddCritterWatchMonitoring(critterWatchUri, systemControlUri);
opts.ListenToSqsQueue("critterwatch-control-trip-service", q =>
{
q.DeadLetterQueueName = "trip-service-dlq";
});3. Disable native DLQs on this transport entirely (rely on Wolverine's durability instead):
opts.UseAmazonSqsTransport(t => t.DisableAllNativeDeadLetterQueues());
opts.AddCritterWatchMonitoring(critterWatchUri, systemControlUri);Same shape applies to other native-DLQ transports
Azure Service Bus and the other transports with a default DLQ behave the same way. Whichever DLQ strategy you pick for the rest of your application's listeners, the queues AddCritterWatchMonitoring installs inherit the same contract — there's no special CritterWatch DLQ to provision.
Native DLQs and CritterWatch management
CritterWatch's Dead Letter Queue explorer manages the durable (database) DLQ only — the failed messages Wolverine persists to its message store. It has no visibility into broker-native dead-letter queues (Amazon SQS DLQ, Azure Service Bus $DeadLetterQueue, RabbitMQ DLX). A message that dead-letters natively stays at the broker and won't appear in CritterWatch until it's forwarded into the Wolverine database.
So for CritterWatch to manage a service's dead letters, those failures need to land in — or be propagated into — the durable store. Two approaches:
A. Send failures straight to the durable store (no native DLQ). Simplest for CritterWatch; it changes where your dead letters live, so weigh it against any existing native-DLQ tooling or alarms you rely on.
// Amazon SQS — disable native DLQs transport-wide; failures go to the
// Wolverine durability database instead.
opts.UseAmazonSqsTransport(t => t.DisableAllNativeDeadLetterQueues());On RabbitMQ the per-endpoint equivalent is the WolverineStorage dead-letter mode (failures bypass the native DLX and go to the durable store).
B. Keep the native DLQ and forward it into the durable store. Preserves your existing native dead-lettering and surfaces those messages in CritterWatch — the better fit when retrofitting onto a running system.
RabbitMQ has this built in — Wolverine listens on the native DLQ, reconstructs each envelope, and writes it to the durability database where CritterWatch can replay or discard it:
csharpopts.UseRabbitMq(/* ... */).EnableDeadLetterQueueRecovery();Amazon SQS / Azure Service Bus don't yet have a built-in equivalent (tracked upstream in wolverine#3103). Until it lands, either use approach A, or stand up a listener on the native DLQ that forwards each message to the store via
IMessageInbox.MoveToDeadLetterStorageAsync(...).
Retrofitting without disrupting the host
Adding CritterWatch shouldn't change queues your application already owns. Keep AutoProvision() scoped (or pre-provision CritterWatch's critterwatch and control queues through your infrastructure automation), and use per-endpoint DLQ settings so CritterWatch never re-declares or alters the host's existing dead-letter infrastructure.
appsettings.json
The recommended approach for managing connection strings:
{
"ConnectionStrings": {
"critterwatch": "Host=localhost;Database=critterwatch;Username=postgres;Password=postgres",
"rabbitmq": "amqp://localhost"
}
}builder.AddCritterWatch(
builder.Configuration.GetConnectionString("critterwatch")!,
opts =>
{
var rabbitUri = new Uri(
builder.Configuration.GetConnectionString("rabbitmq")!);
opts.UseRabbitMq(rabbitUri).AutoProvision();
opts.ListenToRabbitQueue("critterwatch").Sequential();
});Alert thresholds
Most alert thresholds are tuned in the UI rather than in configuration files — see Alert Configuration for the live preview, history tab, and three-level cascade (global → per-service → per-message-type).
Defaults that ship with the console:
| Threshold | Default |
|---|---|
| DLQ count Warning / Critical | 10 / 100 |
| Projection lag Warning / Critical | 30s / 300s |
| Agent unhealthy Warning / Critical | 2 / 5 consecutive checks |
| DLQ rate / hour Warning / Critical | 10 / 50 |
| Failure rate Warning / Critical | 5% / 20% |
| Throughput multiplier Warning / Critical | 3× / 10× of baseline |
| Exec time Warning / Critical | +50% / +200% over baseline |
For services that need different defaults baked in (rather than tuned post-deploy), declare baselines from AddCritterWatchMonitoring — see Registration → Declared Baselines.
