Files
skills/docs/security-stack/security/jws/src/observability/metrics.md
2026-05-29 08:20:56 +01:00

2.9 KiB

metrics

(no submodules)

Purpose

OpenTelemetry instrumentation surface for the JWS verify and sign hot paths. Every production code path records a histogram for wall-time latency and increments outcome-tagged counters; the error registry's recordError also fires once per typed throw so dashboards can pivot on code cardinality. The outcome attribute is bounded to a 4-value enum (success | sig_invalid | alg_not_allowed | header_invalid) so production observability backends do not blow up under attacker-controlled token streams.

Features

  • recordVerify(alg, outcome, durationMs) — histogram + outcome counter.
  • recordSign(alg, "success" | "error", durationMs) — sign-side mirror.
  • recordError(code) — typed-error counter pivoted by code.
  • resetMetricsForTesting() — rebinds the cached instrument bag after a test installs a fresh MeterProvider.
  • getInstrumentsForTesting() — exposes the bag for assertion-time introspection.

Usage: Basic Example

import { metrics } from "@opentelemetry/api";
import { resetMetricsForTesting, compactVerify } from "@suaveplan/jws";

metrics.setGlobalMeterProvider(myInMemoryProvider);
resetMetricsForTesting();
await compactVerify(token, key, { algorithms: ["HS256"] });
// `jws.verify_duration_ms{alg=HS256, outcome=success}` has 1 sample.

Usage: Advanced Example

import { getInstrumentsForTesting, recordVerify, recordError, JWS_CODES } from "@suaveplan/jws";

const bag = getInstrumentsForTesting();
// bag.verifyDurationMs / bag.signDurationMs (Histogram)
// bag.verifyTotal     / bag.signTotal       (Counter)
// bag.errorTotal                            (Counter)

// Bypass the pipeline for an ad-hoc observation:
recordVerify("RS256", "sig_invalid", 4.2);
recordError(JWS_CODES.SIG_INVALID);

API Reference

  • recordVerify(alg: string, outcome: JwsVerifyOutcome, durationMs: number): void
  • recordSign(alg: string, outcome: "success" | "error", durationMs: number): void
  • recordError(code: string): void
  • nowMs(): number
  • resetMetricsForTesting(): void
  • getInstrumentsForTesting(): InstrumentBag
  • type JwsVerifyOutcome = "success" | "sig_invalid" | "alg_not_allowed" | "header_invalid"

Implementation Notes

The instrument bag is cached in module scope so repeated invocations do not call metrics.getMeter on every request — that call has non-trivial overhead under the OTel SDK because it walks the provider's registry. Tests that install a fresh MeterProvider MUST call resetMetricsForTesting() in beforeEach; otherwise the cached bag stays bound to the original (no-op) provider and the test asserts against an empty histogram. The outcome enum's bounded cardinality is deliberate: an unbounded enum would let an adversarial token stream blow up the metrics backend's series count, and most OTel sinks bill by series.