# @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](./src/error-codes/error-codes.md) — This module declares the four stable error codes the stdout audit sink exposes to consumers. Every failure path inside… - [error-codes](./src/error-codes/error-codes.md) — This module declares the four stable error codes the stdout audit sink exposes to consumers. Every failure path inside… - [hmac](./src/hmac/hmac.md) — This module produces the HMAC-SHA-256 tag attached to every audit line. Signing operates on a *canonical* byte represen… - [hmac](./src/hmac/hmac.md) — This module produces the HMAC-SHA-256 tag attached to every audit line. Signing operates on a *canonical* byte represen… - [instruments](./src/instruments/instruments.md) — This module owns the two counters the stdout sink emits at runtime, matching design doc §12.3's metric surface for the… - [instruments](./src/instruments/instruments.md) — This module owns the two counters the stdout sink emits at runtime, matching design doc §12.3's metric surface for the… - [options](./src/options/options.md) — This module owns the Zod schema and defaults for the scalar slice of `createStdoutAuditSink({...})`'s input. Validation… - [options](./src/options/options.md) — This module owns the Zod schema and defaults for the scalar slice of `createStdoutAuditSink({...})`'s input. Validation… - [schema](./src/schema/schema.md) — 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](./src/schema/schema.md) — 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](./src/sink/sink.md) — This module wires the schema, HMAC signer, instruments, and options into a `RequestAuditSink` implementation that write… - [sink](./src/sink/sink.md) — This module wires the schema, HMAC signer, instruments, and options into a `RequestAuditSink` implementation that write… ## Modules - [`./sink`](./src/sink/sink.md) — `createStdoutAuditSink`, the factory that ties everything together. - [`./schema`](./src/schema/schema.md) — `AuditRecord` Zod schema and boundary parser. - [`./hmac`](./src/hmac/hmac.md) — canonical bytes and HMAC-SHA-256 signer / verifier. - [`./instruments`](./src/instruments/instruments.md) — OTel counters (`api.audit.records_total`, `api.audit.dropped_total`). - [`./options`](./src/options/options.md) — scalar option schema + defaults (`maxQueueMs = 100`, `warnThrottleMs = 60_000`). - [`./error-codes`](./src/error-codes/error-codes.md) — `AUDIT_STDOUT.*` registry (`INVALID_RECORD`, `WRITER_FAILED`, `QUEUE_FULL`, `INVALID_OPTIONS`). ## Quick start ```ts 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.