Files
skills/plugins/document-navigator/docs/api-stack/api/api-audit-stdout
2026-06-09 21:06:27 +01:00
..
2026-06-09 21:06:27 +01:00
2026-06-09 21:06:27 +01:00

@suaveplan/api-audit-stdout - Stdout RequestAuditSink for @suaveplan/api-host. Writes JSON-per-line audit records to a Writable (default process.stdout) with an HMAC-SHA-256 signature over canonical bytes. Implements the design-doc §12.5 audit-record shape, Zod-validated at the boundary, with bounded backpressure (maxQueueMs default 100ms), observable drop counter, and a once-per-minute warning throttle. OTel counters api.audit.records_total{sink=stdout,type,outcome} and api.audit.dropped_total{sink=stdout}. Server-tier — Bun + Node.

Stdout audit sink for @suaveplan/api-host. Writes JSON-per-line audit records to a LineWriter (default process.stdout) with an HMAC-SHA-256 signature appended as a _hmac field. Implements RequestAuditSink from @suaveplan/api-host/request-pipeline, so the host's request pipeline accepts the instance directly via its audit capability slot.

The record shape is the design-doc §12.5 canonical wire format — ts, type, principal, tenant, request_id, trace_id, subject, outcome, and a conditional cause. Each record is Zod-validated at the boundary before signing, so a malformed record never reaches the log pipeline.

Documentation tree

  • error-codes — This module declares the four stable error codes the stdout audit sink exposes to consumers. Every failure path inside…
    • error-codes — This module declares the four stable error codes the stdout audit sink exposes to consumers. Every failure path inside…
  • hmac — This module produces the HMAC-SHA-256 tag attached to every audit line. Signing operates on a canonical byte represen…
    • hmac — This module produces the HMAC-SHA-256 tag attached to every audit line. Signing operates on a canonical byte represen…
  • instruments — This module owns the two counters the stdout sink emits at runtime, matching design doc §12.3's metric surface for the…
    • instruments — This module owns the two counters the stdout sink emits at runtime, matching design doc §12.3's metric surface for the…
  • options — This module owns the Zod schema and defaults for the scalar slice of createStdoutAuditSink({...})'s input. Validation…
    • options — This module owns the Zod schema and defaults for the scalar slice of createStdoutAuditSink({...})'s input. Validation…
  • schema — This module locks the wire shape of the JSON line the stdout sink writes. The shape is exactly what design doc §12.5 sp…
    • schema — This module locks the wire shape of the JSON line the stdout sink writes. The shape is exactly what design doc §12.5 sp…
  • sink — This module wires the schema, HMAC signer, instruments, and options into a RequestAuditSink implementation that write…
    • sink — This module wires the schema, HMAC signer, instruments, and options into a RequestAuditSink implementation that write…

Modules

  • ./sink — createStdoutAuditSink, the factory that ties everything together.
  • ./schema — AuditRecord Zod schema and boundary parser.
  • ./hmac — canonical bytes and HMAC-SHA-256 signer / verifier.
  • ./instruments — OTel counters (api.audit.records_total, api.audit.dropped_total).
  • ./options — scalar option schema + defaults (maxQueueMs = 100, warnThrottleMs = 60_000).
  • ./error-codes — AUDIT_STDOUT.* registry (INVALID_RECORD, WRITER_FAILED, QUEUE_FULL, INVALID_OPTIONS).

Quick start

import { createStdoutAuditSink } from "@suaveplan/api-audit-stdout";

const sink = createStdoutAuditSink({
    hmacKey: new TextEncoder().encode(process.env["AUDIT_HMAC_KEY"]!),
});

await sink.emit({
    at: "2026-05-26T10:00:00.000Z",
    requestId: "req_1",
    traceId: null,
    route: "/admin/plugin",
    plugin: "@core/admin",
    method: "POST",
    principal: { id: "u1", scopes: ["admin"] },
    tenantId: "t1",
    status: 200,
    outcome: "success",
});

Drop policy

Backpressure is bounded by maxQueueMs (default 100ms). When the queue wait exceeds the bound the record is dropped, the api.audit.dropped_total{sink,reason} counter increments, and (subject to a once-per-warnThrottleMs throttle) a single warning is logged. Drops are NEVER silent.