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_totaldeferred, 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'spkg/telemetryis 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/authnreplacing the currentpkg/http/controls/authnseams krites pins today).
2. Consumption plan (krites, once the module exists)¶
When transport-metrics ships and krites is on the v0.32 toolkit:
- ✅ Mount
/metrics(DONE 2026-07-22,pkg/cmd/studio/metrics.go). The module'smetrics.Registermounts a Prometheus/metrics(Go-runtime + process +build_infocollectors) 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/apiauth chain (at/metrics, which that chain skips) so a single guard applies, no double-guarding. Gated bydiagnostics.metrics.enabled(off by default); optionalnet/http/pprofunder the same guard viadiagnostics.pprof.enabled. Routes register at launch (toggle → restart). Verified live:401without the credential,200+ full exposition with it. - ✅ Register krites domain collectors (DONE 2026-07-23,
pkg/studio/collectors.go) via the module'sWithCollectorshook, threaded fromServer.Collectors():krites_jobs_active{kind}(scrape-time from the0028registryActive()),krites_ort_session_cached(0/1, the cached face-analyzer session),krites_models_resolved{id}(0/1 cache-presence,0004/0031;models.Cached), andkrites_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 purepkg/studio/jobsengine (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. - Optional pprof behind the session credential only (never a static scrape token — it can dump heap/goroutine state), gated by its own opt-in.
- ✅ 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/metricsvia the session cookie, and an honest empty-state when off (the0027§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 push —
observability'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.