Skip to content

feat(httpapi): Prometheus /metrics endpoint - #123

Merged
prashar32 merged 2 commits into
mainfrom
feat/metrics-endpoint
Jun 13, 2026
Merged

feat(httpapi): Prometheus /metrics endpoint#123
prashar32 merged 2 commits into
mainfrom
feat/metrics-endpoint

Conversation

@prashar32

Copy link
Copy Markdown
Owner

Adds an authenticated Prometheus /metrics endpoint so platform teams can watch RiskKernel's own state alongside everything else they run.

What it exposes

GET /metrics returns the Prometheus text exposition format (version 0.0.4), sourced from the durable store:

  • riskkernel_runs_total{status} — governed runs by lifecycle status (running / halted / cancelled)
  • riskkernel_runs_halted_total{reason} — halted runs by halt reason (token/dollar/loop/time budget, cancelled)
  • riskkernel_spend_dollars_total — total spend in dollars, summed from the cost ledger
  • riskkernel_tokens_total — total prompt + completion tokens
  • riskkernel_model_calls_total — priced model calls recorded in the ledger
  • riskkernel_approvals_pending — pending human-in-the-loop approval-queue depth

Each metric carries proper # HELP and # TYPE lines; counters for monotonic spend/tokens/calls, gauges for the live status/queue counts.

Notes

  • No new dependency. The exposition format is hand-rolled — the surface is tiny and a minimal dependency graph is the rule. No prometheus/client_golang.
  • Honors the no-telemetry posture. This is local metrics the user scrapes: numbers are derived on the fly from the SQLite state the user already owns, nothing is emitted anywhere, and no prompt content or PII is exposed.
  • Authenticated like the rest of the API (bearer token when one is configured), and registered only when a durable store is the source of truth.
  • Output is deterministic (labels and series sorted), and a fresh daemon still emits a zero riskkernel_runs_total series so a scrape never reads an empty metric as a failed scrape.
  • docs/METRICS.md documents the metric list and includes an example Prometheus scrape config.

Tests

New internal/httpapi/metrics_test.go (mirrors the newTestServer harness): seeds a running / halted / cancelled run plus a pending approval, scrapes /metrics, and asserts 200, the text/plain; version=0.0.4 Content-Type, and the expected metric lines and values. Also covers the empty-daemon case and that auth is enforced (401 without the token, 200 with it).

gofmt -l is clean, go vet ./... passes, and go test ./... passes.

Closes #91.

Platform teams want to watch a reliability tool's own state alongside
everything else they run. Add an authenticated GET /metrics endpoint that
emits the Prometheus text exposition format (version 0.0.4) describing the
daemon's governed-run state: runs by lifecycle status, halted runs by halt
reason, total spend in dollars and tokens, priced model calls, and the
pending human-in-the-loop approval-queue depth.

The numbers are derived on the fly from the SQLite store the user already
owns, so this honors the no-telemetry posture: local metrics the user
scrapes, nothing emitted anywhere, no prompt content or PII. The exposition
format is hand-rolled rather than pulling in prometheus/client_golang -
the surface is tiny and a minimal dependency graph is the rule.

The route is registered alongside the other authenticated routes and only
when a durable store is configured. Includes docs/METRICS.md with the
metric list and an example Prometheus scrape config.
@prashar32
prashar32 merged commit 8d35ef4 into main Jun 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prometheus /metrics endpoint for the daemon

1 participant