3.4 KiB
Capability Spec Delta: api-audit-stdout (api-audit-stdout)
Package: @suaveplan/api-audit-stdout
Status: PROPOSED
Every requirement below carries a
**Verified by:**line at authoring time.
ADDED Requirements
Requirement: JSON-per-line writes with HMAC signature
The package SHALL write each audit record as a single JSON line to the supplied writer (default process.stdout). Each line MUST include an HMAC-SHA-256 signature computed over the canonical bytes of the record using the configured hmacKey.
Scenario: written line is JSON with signature
- WHEN
sink.write({ ts: "...", type: "request.error", ... })is called - THEN exactly one line is appended to the writer AND that line parses as JSON AND it contains a
_hmacfield whose value verifies against the canonical bytes
Verified by: src/sink/sink.test.ts::"writes JSON-per-line with HMAC signature"
Requirement: Record shape validated by Zod
The package SHALL validate each record against AuditRecord Zod schema BEFORE writing. Invalid records raise AUDIT_STDOUT_INVALID_RECORD and MUST NOT be written.
Scenario: missing required field rejected
- WHEN a record is missing
request_id - THEN
write()throwsAUDIT_STDOUT_INVALID_RECORDAND no line is written
Scenario: unknown field rejected
- WHEN a record carries an unknown top-level field
- THEN
write()throwsAUDIT_STDOUT_INVALID_RECORD
Verified by: src/schema/schema.test.ts::"missing request_id is rejected" AND src/schema/schema.test.ts::"unknown field is rejected"
Requirement: Backpressure drops are observable
The package SHALL block the caller up to maxQueueMs (default 100ms) and then drop the record with api.audit.dropped_total{sink="stdout"} increment AND a single warning log per minute. Drops MUST NEVER be silent.
Scenario: drop increments dropped_total
- WHEN the writer is paused such that the queue exceeds
maxQueueMs - THEN the record is dropped AND
api.audit.dropped_total{sink="stdout"}increments by 1
Scenario: warning log throttled to once per minute
- WHEN 100 records are dropped within 30 seconds (with
FakeClock) - THEN at most one warning is logged in that minute window
Verified by: src/sink/sink.spec-conformance.test.ts::"drop increments dropped_total" AND src/sink/sink.spec-conformance.test.ts::"warning log throttled to once per minute"
Requirement: Successful writes counted with labels
The package SHALL increment api.audit.records_total{sink="stdout", type, outcome} once per successful write.
Scenario: successful write increments counter
- WHEN a valid record is written
- THEN
api.audit.records_total{sink="stdout", type, outcome}increments by exactly 1 with the record'stypeandoutcomeas labels
Verified by: src/sink/sink.spec-conformance.test.ts::"successful write increments records_total with type and outcome labels"
Requirement: HMAC verification detects tampering
The package SHALL produce HMAC signatures such that any modification to the record's canonical bytes invalidates the signature on verification with the same key.
Scenario: tampering invalidates HMAC
- WHEN a written line's
outcomeis flipped fromsuccesstofailureafter signing - THEN HMAC verification with the original key returns
false
Verified by: src/hmac/hmac.test.ts::"tampering invalidates HMAC"