Keyring

Request logs

What the SDK reports about every request, and exactly what you can see of it today.

Every request the middleware handles produces one usage event: the key, the route, the status, the latency, and the reason if it was denied. The SDK buffers them in a fixed 10,000-event ring buffer and ships them to the control plane gzipped, every second or every 1,000 events. Above 5,000 events per second the SDK samples, but a 4xx, a 5xx or a rate-limited request is never sampled, and every count is divided by its sample rate so a chart never under-reports a sampled fleet.

Telemetry is fire-and-forget with a 2-second timeout and one retry: a control plane that is slow cannot slow your request path. telemetry: false sends nothing at all.

What you can see today

The key's usage chart. A key's page in the dashboard shows requests and errors per day, 30 days by default. The same data:

curl -H "Authorization: Bearer $KEYRING_SECRET_KEY" \
  "https://keyring-api.belghalem.fr/v1/keys/$KEY_ID/usage?days=30"
{
  "object": "usage",
  "key_id": "01a0...",
  "days": 30,
  "data": [{ "day": "2026-09-15", "requests": 1240, "errors": 3 }]
}

The tenant's usage. A tenant's page shows request counts per project and environment per month, from GET /v1/usage/tenants, which a test secret key also reads for its own environment.

Billing. GET /v1/usage/billing and the dashboard's Overview show the billable tenants per month. It reads live rows only, so a test secret key is refused rather than answered with an empty list that would claim you billed nothing.

Denials. A refused request is logged in the environment of the key that was refused, with the reason, so a customer's "my test key is rejected" is in the test-mode data. The reason never reaches the caller: every 401 body is identical.

Not yet

There is no per-request explorer: you cannot yet open a list of individual requests with their route, status and latency in the dashboard, and there is no endpoint that returns them. The rows exist in the control plane's request log, and the aggregates above are computed from them. Until an explorer ships, your own access log beside req.keyring.displayPrefix, which is loggable by construction, is the per-request view.

Redacting keys from your own logs

@keyring/sdk exports redact, redactString and serializers for pino, winston and bunyan that replace anything shaped like a Keyring key, and every field named like a secret, with [redacted]. This runs in the docs' own test suite:

import { redact, redactString } from '@keyring/sdk';

const line = redactString(
  'caller presented kr_live_abcdefghijklmnopqrstuvwxyz012345deadbeef',
);

const entry = redact({
  headers: {
    authorization: 'Bearer kr_test_abcdefghijklmnopqrstuvwxyz012345deadbeef',
  },
  password: 'hunter2',
  tenant: 'acme',
});
{
  "line": "caller presented [redacted]",
  "entry": {
    "headers": { "authorization": "[redacted]" },
    "password": "[redacted]",
    "tenant": "acme"
  }
}

On this page