jwt-flight-recorder
v0.2.0
Published
JWT observability and security monitoring toolkit with safe-by-default telemetry, metrics, and alerting.
Downloads
281
Maintainers
Readme
jwt-flight-recorder
JWT observability and security monitoring toolkit for Node.js.
Product vision:
Know exactly why JWT auth is failing, when tokens are misused, and where security issues are happening without leaking secrets.
Product strategy summary
jwt-flight-recorder focuses on one domain only: JWT lifecycle observability.
- Capture decode, verify, and issue events.
- Redact sensitive fields by default.
- Turn events into telemetry (OpenTelemetry spans/events/attributes).
- Expose operational metrics for SRE/security workflows.
- Trigger rule-based alerts for suspicious auth behavior.
- Integrate with existing Node frameworks with minimal code changes.
Why teams use this
- JWT production failures are often opaque.
- Logging raw tokens is unsafe.
- Security teams need abuse signals, not just logs.
- SRE teams need metrics and trace correlation.
- Developers need wrappers that do not alter application correctness.
Features
- Ring-buffer recorder with bounded memory.
- Safe-by-default token fingerprinting.
- Wrapper utilities for sync and async verifier/signer functions.
- OpenTelemetry mapping and export queue.
- Built-in counters/histograms + Prometheus snapshot output.
- Alert rules with thresholds, windows, grouping, and cooldown.
- Event stream hooks with
on,off,once. - Framework adapters for Express, Fastify, Nest-style modules, and generic HTTP handlers.
- CLI incident workflows: inspect, record, metrics, alerts, tail, export-otel, doctor.
Architecture
Modules:
recorder: ring buffer, event pipeline, hooks, wrapper integration.telemetry: event to OTel span/event mapping, async export queue.metrics: counters and histograms with JSON + Prometheus output.alerts: rule engine and callback dispatch.redaction: claim and field sanitization.config: options + env + JSON/YAML file loading.integrations: adapters for common Node server stacks.exporters: webhook/slack/console handlers and JSONL event exporter.
Install
npm install jwt-flight-recorderNode.js 18+ required.
Quick start
const jwt = require("jsonwebtoken");
const {
createJwtFlightRecorder,
wrapJwtVerifier,
wrapJwtSigner,
} = require("jwt-flight-recorder");
const recorder = createJwtFlightRecorder({
capacity: 2000,
telemetry: {
enabled: true,
useOpenTelemetry: true,
sampleRate: 1,
decodeSampleRate: 0.25,
},
alerts: {
enabled: true,
includeDefaultRules: true,
},
});
const verify = wrapJwtVerifier((token) => jwt.verify(token, process.env.JWT_SECRET), recorder, {
meta: () => ({ integration: "jsonwebtoken" }),
});
const sign = wrapJwtSigner((payload) => jwt.sign(payload, process.env.JWT_SECRET), recorder);
try {
const claims = verify("<jwt>");
const newToken = sign({ sub: "user-1" });
console.log(claims, newToken);
} catch (error) {
// behavior unchanged: original errors still throw
}
console.log(recorder.getMetricsSnapshot());Public API
Core:
createJwtFlightRecorder(options)wrapJwtVerifier(verifyFn, recorder, options)wrapJwtSigner(signFn, recorder, options)parseJwt(token)buildJwtContext(token, options)fingerprintToken(token, length)
Telemetry:
createTelemetryExporter(options)mapEventToTelemetryAttributes(event)
Metrics:
createMetricsRegistry(options)recorder.getMetricsSnapshot()recorder.getPrometheusMetrics(prefix)
Alerts:
createAlertManager(options)DEFAULT_ALERT_RULESrecorder.getAlertHistory(limit)recorder.evaluateAlerts(events)
Hooks:
recorder.on(eventType, handler)recorder.off(eventType, handler)recorder.once(eventType, handler)recorder.emit(eventType, payload)
Redaction and safety:
createRedactor(options)sanitizeClaims(payload, options)sanitizeObject(value, options)safeJsonStringify(value, options)
Config:
loadRecorderConfig(options)loadConfigFile(path)loadEnvConfig(env, prefix)
Integrations:
createExpressJwtObserver(options)createFastifyJwtPlugin(options)createNestJwtFlightRecorderModule(options)createNestJwtFlightRecorderProvider(options)createNestJwtObserver(options)wrapHttpJwtHandler(handler, options)
Event model
Typical event fields:
id,type,timestamp,outcometokenFingerprintissuer,audience,subjectHashexpiryStatus,tokenAgeSecondserrorClass,errorReason,errorMessagerequestContext,requestId,traceIdmetadata
No raw token is present unless both:
includeRawToken=truesecureDebugMode=true
OpenTelemetry mapping
JWT events map to spans/events:
- verify events ->
jwt.verifyspan - issue events ->
jwt.issuespan - decode events ->
jwt.decodespan/event (samplable)
Safe attributes include:
jwt.token.fingerprintjwt.issuerjwt.subject.hashjwt.audiencejwt.expiry.statusjwt.error.classjwt.error.reason- request and trace correlation ids
Built-in metrics
Counters:
verify_success_totalverify_failure_totaltoken_issued_totaldecode_failure_totalexpired_token_totalinvalid_signature_totalunknown_issuer_totalrefresh_failure_total
Histograms:
auth_latency_mstoken_age_seconds
Export options:
- JSON snapshot via API/CLI
- Prometheus text format via API/CLI
- OpenTelemetry metrics through meter integration
Alerting
Rule capabilities:
- threshold + time window
- severity levels
- grouping keys
- cooldown periods
- custom filter logic
- callback hooks
Pluggable callbacks:
- console
- webhook
- slack webhook
- custom function
Default rules include detection for:
- repeated verify failures
- expired token spikes
- unknown issuer activity
- invalid signature bursts
- decode error bursts
- suspicious token reuse
- issuance volume spikes
Security model
Production-safe defaults:
includeRawToken=false- claim redaction enabled
- sensitive field filtering enabled
- telemetry/export failures isolated from app correctness
Important warning:
parseJwt is for observability only and must never be used for trust decisions.
See SECURITY.md for detailed guidance.
Framework integrations
Express
const { createExpressJwtObserver } = require("jwt-flight-recorder");
app.use(
createExpressJwtObserver({
recorder,
verifyToken: (token) => jwt.verify(token, process.env.JWT_SECRET),
failOpen: false,
})
);Fastify
const { createFastifyJwtPlugin } = require("jwt-flight-recorder");
fastify.register(
createFastifyJwtPlugin({
recorder,
verifyToken: (token) => jwt.verify(token, process.env.JWT_SECRET),
failOpen: false,
})
);Nest-style provider/module
const {
createNestJwtFlightRecorderModule,
createNestJwtFlightRecorderProvider,
} = require("jwt-flight-recorder");CLI
Inspect token safely:
jwt-flight-recorder inspect <jwt>Record event to JSONL:
jwt-flight-recorder record <jwt> --event verify.failure --out jwt-events.jsonlMetrics from JSONL:
jwt-flight-recorder metrics --in jwt-events.jsonl --prometheusEvaluate alerts:
jwt-flight-recorder alerts --in jwt-events.jsonl --jsonTail stream:
jwt-flight-recorder tail --in jwt-events.jsonlExport telemetry mapping:
jwt-flight-recorder export-otel --in jwt-events.jsonl --jsonRun diagnostics:
jwt-flight-recorder doctor --config ./examples/jwt-flight-recorder.config.jsonConfiguration
You can configure via:
- JavaScript options
- env vars (prefix
JFR_) - config file (
.json,.yaml,.yml)
Example file:
examples/jwt-flight-recorder.config.json
Common env vars:
JFR_CAPACITYJFR_INCLUDE_RAW_TOKENJFR_SECURE_DEBUG_MODEJFR_TELEMETRY_ENABLEDJFR_OTEL_ENABLEDJFR_SAMPLE_RATEJFR_DECODE_SAMPLE_RATE
Migration from 0.1.x
0.2.x keeps wrapper concepts but adds modular observability features.
Recommended migration:
- Replace basic recorder construction with explicit telemetry/alert config.
- Keep raw token logging disabled in production.
- Wire
getMetricsSnapshot()andgetAlertHistory()into dashboards. - Add framework adapter where request context is needed.
- Enable OTel progressively and monitor exporter queue stats.
Development
npm test
npm run audit
npm run pack:checkExamples
License
MIT
