User Story
As a relay operator, I want pyrycode_relay_connected_binaries and pyrycode_relay_connected_phones gauges to track live connection counts in real time, so I can graph utilization and alert on unexpected drops from Prometheus instead of polling /healthz.
Context
/healthz (#10) reports current connection counts as a one-shot snapshot. Time-series visibility into connection lifecycle (joins, leaves, grace expiries) requires Prometheus gauges wired at the registry's mutation points.
This ticket wires the cheapest pair of metrics — connection counters — into the existing register / unregister / grace-expiry-eviction sites in internal/relay/registry.go. The gauges live on the metrics registry introduced by the scaffolding slice (#59); this ticket adds the gauge definitions and the Inc/Dec wiring.
The gauges are independent of the listener — once they're wired, they reflect live counts in the registry whether or not anyone is scraping. The listener slice (#60) is a separate sibling.
Acceptance Criteria
Technical Notes
Size Estimate
S
Split from #56. Depends on the scaffolding slice (#59, closed 2026-05-11).
User Story
As a relay operator, I want
pyrycode_relay_connected_binariesandpyrycode_relay_connected_phonesgauges to track live connection counts in real time, so I can graph utilization and alert on unexpected drops from Prometheus instead of polling/healthz.Context
/healthz(#10) reports current connection counts as a one-shot snapshot. Time-series visibility into connection lifecycle (joins, leaves, grace expiries) requires Prometheus gauges wired at the registry's mutation points.This ticket wires the cheapest pair of metrics — connection counters — into the existing register / unregister / grace-expiry-eviction sites in
internal/relay/registry.go. The gauges live on the metrics registry introduced by the scaffolding slice (#59); this ticket adds the gauge definitions and the Inc/Dec wiring.The gauges are independent of the listener — once they're wired, they reflect live counts in the registry whether or not anyone is scraping. The listener slice (#60) is a separate sibling.
Acceptance Criteria
pyrycode_relay_connected_binariespyrycode_relay_connected_phonesinternal/relay/registry.go. The architect picks the exact shape (counters maintained inside the registry's lock vs. a pull-basedprometheus.Collectorover the existingCounts()).-racetest that races register/unregister/grace-expiry against periodic gauge reads).docs/PROJECT-MEMORY.md) must not change the gauge.make vet,make test -race, andmake buildclean.docs/knowledge/codebase/<n>.mdsummary entry created;docs/knowledge/INDEX.mdupdated.Technical Notes
NewMetricsRegistry) and handler factory (NewMetricsHandler) live ininternal/relay/metrics.gofrom relay: adopt prometheus/client_golang and introduce metrics registry scaffolding #59. Consume that seam; do not re-create the registry.docs/knowledge/features/metrics-registry.md§ "Seam shape — per-concern collector struct". A separate file (e.g.metrics_connections.go) with a private struct holding the two gauges and a constructor takingprometheus.Registerermatches the established pattern; sibling tickets (relay: /metrics — upgrade-attempt and register-failure counters #57, relay: /metrics — frame-forwarded and grace-expiry counters #58) will follow the same shape.docs/PROJECT-MEMORY.mdline 41: "Interface methods called under the lock are documented as non-blocking getters" —prometheus.Gauge.Inc/Decis non-blocking and safe to call under the lock).Size Estimate
S
Split from #56. Depends on the scaffolding slice (#59, closed 2026-05-11).