Skip to content

0030 — Diagnostics: consume the toolkit's scrapeable-metrics module (not in-tree)

  • Status: IMPLEMENTED (2026-07-23). krites (on the v0.32 toolkit) consumes gitlab.com/phpboyscout/go/transport-metrics: a guarded, scrapeable /metrics (+ optional pprof) on the studio, opt-in and off by default (§2.1); the krites domain collectors (§2.2 — jobs_active, ort_session_cached, models_resolved, cull_frames_processed_total; jobs_total deferred, see §2.2); and the Settings→Diagnostics tab with a live heap/goroutines/RSS mini-view (§2.4). Verified live end-to-end. History below records the redirect decision.
  • Why now / the need: the studio is a long-lived local service doing minutes of CPU-heavy ONNX inference over thousands of frames, holding a cached ORT session (0028) plus preview/edit caches. "Is it leaking? what is it doing to memory right now?" has no answer today. GTB's pkg/telemetry is product/usage analytics (event backends incl. OTLP push), not a scrapeable process surface — no /metrics, no Go runtime collectors (heap, GC, goroutines), no process collectors (RSS, FDs, CPU), no pprof.

1. Decision (2026-07-21) — build once, in the toolkit

The original draft of this spec proposed a krites-native pkg/diagnostics + /api/v1/metrics endpoint. Rejected in favour of building it once in the framework, because:

  • GTB's functionality has been extracted into the phpboyscout/go/* toolkit (observability, transport, transit, authn, controls, …). A diagnostics surface is generic service plumbing every downstream tool (keryx, …) needs — it belongs there, not hand-rolled per tool.
  • A major v0.32 overhaul of go-tool-base is coming that brings these modules to krites anyway; an interim in-tree implementation would be throwaway work to be ripped out on the upgrade.

Where it lands (reviewed 2026-07-21)

Neither existing module is the right home: observability is OTLP-push only (OTel SDK) and a Prometheus pull exporter + promhttp/pprof would muddy it; transport/http owns operational endpoints (health/liveness/readiness) and auth placement but should not carry promhttp/pprof dep weight for every consumer; transit/authn/controls are middleware / auth / lifecycle, not metrics.

Resolved: a dedicated phpboyscout/go/transport-metrics module grouping with transport/transit/authn — Prometheus /metrics (promhttp + NewGoCollector()/NewProcessCollector() + a custom-collector hook) and optional net/http/pprof, as framework-free handlers that compose with transport/http's auth chain. This isolates the client_golang/pprof dependency weight from the other modules and keeps observability purely OTel.

  • Tracked at: go-tool-base work item #2 (the original metrics FR, refined 2026-07-21 with this post-extraction direction).
  • Depends on: the v0.32 toolkit migration (transport/controls/authn replacing the current pkg/http/controls/authn seams krites pins today).

2. Consumption plan (krites, once the module exists)

When transport-metrics ships and krites is on the v0.32 toolkit:

  1. ✅ Mount /metrics (DONE 2026-07-22, pkg/cmd/studio/metrics.go). The module's metrics.Register mounts a Prometheus /metrics (Go-runtime + process + build_info collectors) on the studio, guarded by a dedicated session-auth middleware (sessionGuard) built from the per-launch token — accepting the same cookie the browser holds or a bearer. It sits outside the /api auth chain (at /metrics, which that chain skips) so a single guard applies, no double-guarding. Gated by diagnostics.metrics.enabled (off by default); optional net/http/pprof under the same guard via diagnostics.pprof.enabled. Routes register at launch (toggle → restart). Verified live: 401 without the credential, 200 + full exposition with it.
  2. ✅ Register krites domain collectors (DONE 2026-07-23, pkg/studio/collectors.go) via the module's WithCollectors hook, threaded from Server.Collectors(): krites_jobs_active{kind} (scrape-time from the 0028 registry Active()), krites_ort_session_cached (0/1, the cached face-analyzer session), krites_models_resolved{id} (0/1 cache-presence, 0004/0031; models.Cached), and krites_cull_frames_processed_total (counter, incremented delta-wise by the cull runner). Bounded labels only. krites_jobs_total{kind,state} deferred — it would force a Prometheus counter into the pure pkg/studio/jobs engine (no terminal-job query exists post-retention); the active gauge + frames counter cover the operational need without that coupling. A later change can add it via an injected counter option on the registry if outcome rates are wanted.
  3. Optional pprof behind the session credential only (never a static scrape token — it can dump heap/goroutine state), gated by its own opt-in.
  4. ✅ Settings → Diagnostics (DONE 2026-07-23, SettingsDiagnostics.svelte + GET/PUT /api/v1/settings/diagnostics): toggles for metrics/pprof (with an "applies on restart" note — routes register at launch), the scrape URL, a live heap / goroutines / RSS mini-view polled from /metrics via the session cookie, and an honest empty-state when off (the 0027 §8.2 faces-off pattern).

Everything off by default, opt-in per capability, disclosed (0001 §5, R-PRIV-*).

3. Non-goals

  • A krites-native metrics stack — explicitly rejected (§1); consume the module.
  • Product / usage analytics — GTB telemetry's job.
  • Dashboards, alerting, retention — Prometheus/Grafana's job; krites only exposes metrics.
  • Distributed tracing / OTLP pushobservability's remit, not this.

4. Requirements (R-DIAG-*, to fold into 0002 when krites adopts the module)

  • R-DIAG-1 — Metrics and pprof are off by default; each a separate, disclosed opt-in.
  • R-DIAG-2 — When enabled, the studio serves the module's Prometheus /metrics (Go-runtime + process collectors) behind the session auth chain.
  • R-DIAG-3 — No unauthenticated exposure of process internals; pprof takes the session credential only.
  • R-DIAG-4 — krites domain metrics carry bounded label sets.