47 lines
3.0 KiB
Markdown
47 lines
3.0 KiB
Markdown
# `@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 an `AuditSink`. Writes JSON-per-line with HMAC signature to `path`. Rotation policy: size-based (`maxSizeBytes`, default 100 MB) AND time-based (`maxAgeMs`, default 1 day). Rotated files are renamed to `<path>.<utc-ISO>.log` and optionally gzipped if `compress: true`.
|
|
- ADDED: Retention policy — `maxFiles` (default 30) keeps the most recent N rotated files; older files are deleted with a `api.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-open `fd` model gated by `host.audit.file.appendMode` (`"strict-sync" | "append"`).
|
|
- ADDED: Same backpressure model as `api-audit-stdout`: bounded queue, observable drops via `api.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 exceed `maxSizeBytes`) and time rotation (advance `FakeClock` past `maxAgeMs`); assert rotated file exists with the expected timestamp suffix.
|
|
- `src/retention/retention.test.ts` — create N+1 rotated files (with `FakeClock`), assert oldest is evicted and `api.audit.rotation_evicted_total` increments.
|
|
- `src/sink/sink.spec-conformance.test.ts` — drop policy mirrors `api-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"`.
|