Skip to main content
← Back to list
01Issue
FeatureTriagedSwamp CLI
Assigneesstack72

Relationships

#1613 Cluster health snapshots with SSE streaming for swamp serve

Opened by stack72 · 8/11/2026

Problem

Operators building health dashboards for swamp serve have no single endpoint to get a comprehensive, continuously updated view of whether a node is healthy, how much work it is processing, and which dependency is responsible for degradation.

Today's /health endpoint returns unconditional ok with limited schedule and webhook configuration data. /ready is a one-shot startup latch that never reverts to not-ready even if critical components fail. UIs must combine multiple diagnostic WebSocket RPCs and polling calls, but still cannot calculate trustworthy fleet-wide throughput, latency, or dependency health.

Proposed Solution

Add three new authenticated HTTP endpoints to swamp serve, each reporting per-node health (no fleet aggregation — the UI composes the fleet view from N nodes):

1. GET /api/v1/health — Health Snapshot

Returns a comprehensive point-in-time JSON snapshot including:

  • Instance identity: instanceId, deployment mode, uptime, readiness state
  • Active and queued runs: run IDs, kinds (workflow-run/method-run), resource names, durations, principals
  • Run throughput metrics: sliding-window (5 min) counters for completions, failures, cancellations, throughput per minute, and latency percentiles (P50/P95/P99)
  • Workers: name, status (idle/busy/unverified/draining), connected state, capacity, active dispatch count, platform
  • Scheduling: enabled state, schedule entries with cron expressions, next run times, running status
  • Webhooks: registered routes, target workflows, verification schemes
  • Component health: individually checked with timeouts — datastore (reachability + latency), vault (availability), and any future dependencies. Each reports healthy/unhealthy, message, latency, and details

Requires bearer token authentication + admin authorization (same pattern as the existing /api/v1/cancel endpoint).

2. GET /api/v1/health/stream — SSE Health Stream

A Server-Sent Events stream that pushes health snapshots at a configurable interval:

  • Query param ?interval=<ms> controls push frequency (default 5000ms, min 1000, max 60000)
  • Supports Last-Event-ID header for resumable connections
  • SSE format: id: <n>\nevent: health\ndata: <json>\n\n
  • Sends initial snapshot immediately on connect, then at interval
  • Includes X-Accel-Buffering: no header for nginx reverse proxy compatibility
  • Same bearer token + admin auth as the snapshot endpoint

SSE is chosen over WebSocket because this is a read-only, server-to-client push — exactly what SSE was designed for. It also has a much lower integration bar for dashboard tools (curl, browser EventSource, Grafana HTTP datasource) compared to the WebSocket protocol.

3. GET /internal/runs — Full Run History

Returns complete run history from the RunTrackerStore (SQLite-backed), including all completed, failed, and cancelled runs with their full metadata.

  • Disabled by default — requires --enable-internal-api flag (or enable-internal-api: true in serve config, or SWAMP_ENABLE_INTERNAL_API=true env var)
  • Returns 404 when the flag is not set
  • Same bearer token + admin auth

This separates the "how is this node doing" aggregate view (on /api/v1/health) from the "what has this node done" detail view, keeping the health snapshot lean on busy nodes.

Existing endpoints unchanged

  • /health and /ready remain as lightweight, unauthenticated Kubernetes liveness and readiness probes
  • All existing WebSocket diagnostic commands (doctor.*, run.history, worker.list, etc.) continue to work

Implementation Notes

New instrumentation required

No throughput or latency metrics are tracked today. A sliding-window RunMetricsTracker needs to be added and wired into the run completion lifecycle — specifically the scheduled execution event handlers (schedule_completed/schedule_failed), webhook event handlers (webhook_completed/webhook_failed), and detached-run deregistration points.

Datastore health checking

No DatastoreVerifier is held in the serve handler's closure today — the doctor commands create verifiers on-the-fly. The health collector needs either a factory function or a persistent verifier instance constructed from datastoreConfig at startup.

Auth helper extraction

The bearer token + admin policy check pattern is currently inlined in the cancel endpoint. With three new endpoints using the same pattern, it should be extracted into a reusable helper to reduce duplication.

No new SSE library needed

Deno's native ReadableStream is sufficient for SSE — it's a text/event-stream response with formatted text pushed via a stream controller. No external dependencies required.

Alternatives Considered

WebSocket instead of SSE: The codebase already has mature WebSocket infrastructure (~70 commands). However, SSE is a better fit for this use case: it's read-only server-to-client push, requires no handshake negotiation, and has native support in curl, browser EventSource, and monitoring tools. Adding a WebSocket command would force dashboard integrators to implement the full WebSocket protocol.

Fleet aggregation: Each node could aggregate health from all peers via the control-plane store. This was rejected in favor of per-node reporting because it avoids leader-election complexity and lets the UI compose the fleet view with full control over layout and refresh cadence. The instanceId on each response provides a stable correlation key.

Full run history on /api/v1/health: Including all completed runs on the main health endpoint would bloat the response on busy nodes. The three-tier split (aggregate counters on health, full history on internal endpoint, existing run.history on WebSocket) gives operators the right level of detail at each layer.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW

Triaged

8/11/2026, 11:18:43 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack728/11/2026, 11:15:37 PM

Sign in to post a ripple.