Skip to content

SignalR Integration

CritterWatch uses SignalR to push live updates from the console to the browser. As an operator, you don't need to know any of this — the browser handles it. As an integrator who wants to consume the live feed from your own client, this page covers the contract.

Where it lives

The SignalR hub is mounted at /api/messages (configurable via UseCritterWatch(signalRRoute: "...")). It's a one-way push channel — the browser receives messages but never sends commands over SignalR. All operator commands go through the HTTP API at /api/critterwatch/*.

Connection lifecycle

The browser connects via the standard @microsoft/signalr JavaScript client with automatic reconnect. On reconnect, the client requests a full state snapshot from the HTTP API to catch up on anything missed during the disconnection window.

The connection indicator in the header tells you which state you're in:

  • Green — connected and receiving live updates.
  • Yellow — reconnecting (brief blip, automatic recovery).
  • Red — disconnected; the page is showing stale data.

What gets pushed

Every event that should reach the browser is relayed onto the hub immediately after the console persists it. The major message types:

MessageWhat it carries
service_updatedFull service state snapshot (after a telemetry batch is applied)
agent_health_updatedAgent health changes
alert_raised / alert_elevated / alert_resolved / alert_clearedAlert lifecycle
shard_states_updatedProjection shard sequences
persistence_counts_updatedInbox / outbox / scheduled / DLQ counts
back_pressure_triggered / circuit_breaker_trippedEndpoint resilience events
timeline_entryActivity timeline entry

Plus on-demand response messages — handler source code, HTTP chain source code, pending-migration probe results, tenant list refresh — surfaced when the browser opens the corresponding detail page.

Wire format

All messages use the Wolverine CloudEvents envelope:

json
{
  "type": "service_updated",
  "data": {
    "serviceName": "trip-service",
    ...
  }
}

The type discriminator is snake_case derived from the C# message type. Properties inside data are camelCase. Enums serialize as string names ("Query", not 1).

This serialization is configured by the Wolverine SignalR transport and applies to every message on the hub.

Subscribing from a custom client

If you want a server-side or out-of-browser client to subscribe to the live feed:

  1. Add the standard SignalR client for your platform.
  2. Connect to https://<your-console>/api/messages (or your custom route).
  3. Authenticate the same way browsers do — typically via a cookie or bearer token, depending on how you've configured authentication on the console.
  4. Listen for the message types above.

The hub uses a single dispatch method so most platforms handle it via a generic on(type, handler) registration. For .NET clients (Microsoft.AspNetCore.SignalR.Client), you'd register handlers for each type you care about.

Throttling and back pressure

The console doesn't currently throttle the SignalR push rate. A single very busy service can drive a high message rate per connection — keep that in mind if you're building a custom client that does heavy per-message work. The browser SPA buffers updates per Pinia store and reconciles them on the next reactivity tick, which is what keeps the UI smooth on bursty services.

If you're seeing browser CPU or network spikes, see Store Inspector → message log to confirm which message types are noisy.

Released under the MIT License.