first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# WebAuthn observability — metrics
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines every OTel instrument emitted by `@suaveplan/webauthn`. The registry is a single bag of pre-created `Histogram` / `Counter` instances cached at module-load time so production paths can record without per-call allocation. The cache resets via `resetMetricsForTesting()` so a fresh in-memory `MeterProvider` registered in `beforeEach` produces deterministic assertions.
## Features
- Five instruments cover every production path: `webauthn.register_duration_ms`, `webauthn.authenticate_duration_ms`, `webauthn.outcome_total`, `webauthn.attestation_format_total`, `webauthn.errors_total`.
- Bounded label cardinality: `outcome` ∈ `{ ok | error | aborted }`, `op` ∈ `{ register | authenticate }`, `fmt` ∈ the seven IANA-registered formats plus `unknown`.
- `recordRegister` / `recordAuthenticate` write both the duration histogram and the outcome counter atomically so dashboards never disagree.
- `resetMetricsForTesting` / `getInstrumentsForTesting` keep test hooks out of the runtime module surface but available to spec tests.
## Usage: Basic Example
```ts
import {
nowMs,
recordRegister,
} from "@suaveplan/webauthn/observability/metrics";
const start = nowMs();
try {
await doRegister();
recordRegister("ok", nowMs() - start);
} catch (err) {
recordRegister("error", nowMs() - start);
throw err;
}
```
## Usage: Advanced Example
```ts
import { MeterProvider, InMemoryMetricExporter } from "@opentelemetry/sdk-metrics";
import { metrics } from "@opentelemetry/api";
import {
resetMetricsForTesting,
recordAttestationFormat,
} from "@suaveplan/webauthn/observability/metrics";
const exporter = new InMemoryMetricExporter();
const provider = new MeterProvider({ readers: [/* periodic reader */] });
metrics.setGlobalMeterProvider(provider);
resetMetricsForTesting();
recordAttestationFormat("packed");
const points = await exporter.collect();
```
## API Reference
- `WebAuthnOutcome` — `"ok" | "error" | "aborted"`.
- `WebAuthnOp` — `"register" | "authenticate"`.
- `WebAuthnAttestationFmt` — `"packed" | "fido-u2f" | "tpm" | "android-key" | "android-safetynet" | "apple" | "none" | "unknown"`.
- `recordRegister(outcome, durationMs)` — record both histogram + outcome counter.
- `recordAuthenticate(outcome, durationMs)` — same for the assertion path.
- `recordAttestationFormat(fmt)` — increment dispatch counter.
- `recordError(code)` — increment `webauthn.errors_total{code}`.
- `nowMs()` — wall-clock reader.
- `resetMetricsForTesting()` / `getInstrumentsForTesting()` — test hooks.
## Implementation Notes
- The instrument bag is lazily created on first call so the meter resolves against whatever `MeterProvider` the host application has installed.
- The reset hook is mandatory because OTel meter caching makes pre-registered instruments outlive a test's MeterProvider swap; without it a second test reads metrics from the first test's exporter.
- `nowMs()` is exposed as a function rather than `Date.now` directly so future tests can stub it without monkey-patching `globalThis.Date`.