Skip to content

Hosting Guide

CritterWatch supports several hosting topologies. Choose based on your team's needs and infrastructure constraints.

The most common production topology. CritterWatch runs as its own dedicated web application, separate from the services it monitors.

Advantages:

  • Clear separation of concerns
  • Isolated failure domain — if CritterWatch crashes, monitored services are unaffected
  • Independently scalable and deployable
  • Simpler security boundary
cs
// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.AddCritterWatch(
    builder.Configuration.GetConnectionString("critterwatch")!,
    opts =>
    {
        opts.UseRabbitMq(new Uri(builder.Configuration.GetConnectionString("rabbitmq")!))
            .AutoProvision();
        opts.ListenToRabbitQueue("critterwatch").Sequential();
    });

var app = builder.Build();
app.UseCritterWatch();
app.Run();

The embedded Vue SPA is served from the server's root path. Navigate to https://your-critterwatch-host/ to open the console.

Embedded in an Existing Application

CritterWatch can run embedded inside your own Wolverine application as a dev-time assistant: the app monitors itself in-process over a local:// queue (no broker, no separate console process), and the console's own data resets on boot so it always reflects the current configuration. AddCritterWatchEmbedded registers it without seizing your host's identity or codegen mode, and hostOwnsPrimaryStore: true attaches the console's ancillary store to your app's existing Marten/Polecat integration rather than registering a second primary.

This works against either backing store: reference the CritterWatch package for a Marten-backed host, or CritterWatch.SqlServer for a Polecat / SQL Server-backed host — the AddCritterWatchEmbedded API is identical. Runnable examples live in src/Samples/EmbeddedDemo/ (EmbeddedDemo.Marten and EmbeddedDemo.Polecat).

Pass spaPathBase to MapCritterWatchEmbedded (e.g. "/critterwatch") to mount the console's SPA under a sub-path so its client-side-routing fallback doesn't hijack your app's own routes — the HTTP API and SignalR hub stay at the server root. Omit it to serve the console at the host root.

cs
// Embed the CritterWatch console INSIDE your own Wolverine app as a dev-time assistant: the app
// monitors itself in-process over a local:// queue (no broker, no separate console process), and
// the console's own data resets on boot so it always reflects the current configuration.
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("app")!;

builder.Host.UseWolverine(opts =>
{
    // Your app's OWN primary store for its business data
    opts.Services.AddMarten(m => m.Connection(connectionString)).IntegrateWithWolverine();

    // Embed CritterWatch alongside. hostOwnsPrimaryStore: true attaches the console's ancillary
    // store to your existing integration above instead of registering a clashing second primary.
    // (Marten shown here; CritterWatch.SqlServer exposes the same AddCritterWatchEmbedded API for a
    // Polecat / SQL Server-backed host — see the EmbeddedDemo.Marten / EmbeddedDemo.Polecat samples.)
    opts.AddCritterWatchEmbedded(connectionString, hostOwnsPrimaryStore: true);
});

builder.Services.AddWolverineHttp();

var app = builder.Build();

// Mount the embedded console. spaPathBase mounts the SPA under a sub-path (open it at /critterwatch)
// so its client-side-routing fallback doesn't hijack your app's own routes; the HTTP API + SignalR
// stay at the server root. Omit it (or pass "") to serve the console at the host root.
app.MapCritterWatchEmbedded(spaPathBase: "/critterwatch");

app.Run();

Route Conflicts

When embedding CritterWatch in an existing application, ensure there are no route conflicts with your application's existing endpoints. CritterWatch registers routes under /api/critterwatch/* by default.

.NET Aspire Orchestration

For development and staging environments, .NET Aspire provides the best developer experience. It automatically manages service connections, environment variables, and the Vue dev server.

csharp
// In your AppHost Program.cs
var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("postgres");
var rabbit = builder.AddRabbitMQ("rabbit");

// Your monitored services
var tripService = builder.AddProject<Projects.TripService>("trip-service")
    .WithReference(postgres)
    .WithReference(rabbit);

// CritterWatch server
var critterwatch = builder.AddProject<Projects.CritterWatchBff>("critterwatch")
    .WithReference(postgres)
    .WithReference(rabbit)
    .WaitFor(tripService);

builder.Build().Run();

The Projects.* types are generated by Aspire's source generator from the project references in your AppHost. See src/BffHost/Program.cs in this repo for a worked example with the full sample-services lineup.

During Aspire-hosted development, the Vue frontend runs as a separate Vite dev server (port 5173) that proxies to the CritterWatch backend. The embedded SPA middleware is not used in this mode.

Building the NuGet Package with Embedded Frontend

When publishing the CritterWatch NuGet package (or distributing as a private package), build with the EmbedFrontend property to bundle the Vue SPA:

bash
dotnet pack src/CritterWatch.Services -p:EmbedFrontend=true

Or for a regular build:

bash
dotnet build src/CritterWatch.Services -p:EmbedFrontend=true

Without -p:EmbedFrontend=true, the frontend is not embedded — it must be served by a separate dev server (the Aspire configuration handles this automatically).

Production Considerations

HTTPS

Always serve CritterWatch over HTTPS in production. CritterWatch gives operators significant control over your services — plaintext communication is a security risk.

cs
// Enforce HTTPS redirection
app.UseHttpsRedirection();
app.UseHsts();
app.UseCritterWatch();

Authentication

CritterWatch does not ship with built-in authentication. Add ASP.NET Core authentication middleware before UseCritterWatch():

cs
// Register authentication/authorization BEFORE UseCritterWatch so every
// CritterWatch endpoint — its HTTP API and the SignalR hub it maps
// internally — sits behind your auth middleware.
app.UseAuthentication();
app.UseAuthorization();

app.UseCritterWatch();

Network Isolation

In production, CritterWatch should not be publicly accessible. Place it behind a VPN, internal load balancer, or IP allowlist. The transport (RabbitMQ) connecting CritterWatch to your services should also be on an internal network.

Scaling

CritterWatch runs single-node by default — no extra configuration needed for small deployments. (Cluster partitioning is technically on even on a single node, but with one node there's nothing to distribute, so there's nothing to configure; pass enableClusterPartitioning: false to AddCritterWatchServices to switch it off entirely.) For horizontal scaling, the console supports a clustered topology (2+ BFF nodes behind a load balancer): per-service partitioning prevents two nodes from processing the same service's updates concurrently, leader-pinned ticks ensure alert evaluators and metrics scrapers fire exactly once across the cluster, and a Redis SignalR backplane fans push messages to every connected browser regardless of which node owns the WebSocket. See Clustering for the full topology, the opt-in configuration, and the trade-offs.

For single-node deployments — the default — use a process manager (systemd, Kubernetes) to restart CritterWatch automatically on failure.

Health Checks

Register ASP.NET Core health checks to monitor CritterWatch itself:

csharp
builder.Services.AddHealthChecks()
    .AddNpgSql(connectionString, name: "critterwatch-db");

app.MapHealthChecks("/health");

Free for read-only monitoring. A commercial license is required for administrative actions and the MCP server.