3.0 KiB
3.0 KiB
@suaveplan/api-audit-file — file-rotating audit sink
Package: @suaveplan/api-audit-file
Status: PROPOSED
Type: new-package
Stage: 8 — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §12.5, some operators (single-host deployments, on-prem environments without a log shipper) need an audit sink that writes to a local file with rotation. This package is that sink. It mirrors the api-audit-stdout contract (audit capability, JSON-per-line, HMAC-signed records) and adds size + time rotation.
What changes
- ADDED:
createFileAuditSink({ path, hmacKey, clock, rotation })— returns anAuditSink. Writes JSON-per-line with HMAC signature topath. Rotation policy: size-based (maxSizeBytes, default 100 MB) AND time-based (maxAgeMs, default 1 day). Rotated files are renamed to<path>.<utc-ISO>.logand optionally gzipped ifcompress: true. - ADDED: Retention policy —
maxFiles(default 30) keeps the most recent N rotated files; older files are deleted with aapi.audit.rotation_evicted_total{sink="file"}increment. - ADDED: Atomic-write strategy — writes go through a temp file path then
fsync+ rename for crash safety, OR an append-on-openfdmodel gated byhost.audit.file.appendMode("strict-sync" | "append"). - ADDED: Same backpressure model as
api-audit-stdout: bounded queue, observable drops viaapi.audit.dropped_total{sink="file"}. - ADDED: Error codes (
AUDIT_FILE_*):INVALID_RECORD,WRITER_FAILED,QUEUE_FULL,ROTATION_FAILED,RETENTION_FAILED,PATH_NOT_WRITEABLE.
Out of scope
- Cross-host audit aggregation — separate package.
- Encryption at rest — caller handles (the sink writes plaintext + HMAC; disk-encryption is outside scope).
- Centralised log shipping (
fluentd,vector, …) — operators wire the rotated files into those tools.
Source mapping
Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
File-rotating audit sink (api-audit-file) |
§12.5 |
| HMAC-signed records | §12.5 |
Same audit capability contract |
§12.5 |
Verification approach
src/sink/sink.test.ts— write 1000 records, assert all are present, JSON-parseable, HMAC-valid.src/rotation/rotation.test.ts— exercise size rotation (write enough bytes to exceedmaxSizeBytes) and time rotation (advanceFakeClockpastmaxAgeMs); assert rotated file exists with the expected timestamp suffix.src/retention/retention.test.ts— create N+1 rotated files (withFakeClock), assert oldest is evicted andapi.audit.rotation_evicted_totalincrements.src/sink/sink.spec-conformance.test.ts— drop policy mirrorsapi-audit-stdout; observability assertions.tests/dep-graph.test.ts— runtime deps (api-types,crypto-hash,error,logger,zod).
Migration impact
None — additive only. api-host registers this when host.audit.sink = "file".