feat(capture): two-phase classification-aware AuditSink port (#54)
Splits the audit port into Reserve (before any write) + Record (after), so "confidential + sink-down → refuse before any write" is literally true even though the audit record — which lists what landed — can only be written afterwards. - AuditSink.Reserve(ctx, level) → AuditOutcome | error. The error path refuses the capture before writing: confidential + central sink down, or the all-tiers floor (nothing can record). - AuditSink.Record(ctx, entry, outcome) persists per the reserved outcome. - Service: I5 gate runs after the I1 gate and after the dry-run short-circuit (dry-run never probes the sink). AuditBuffered surfaces on the receipt. New ErrAuditUnavailable sentinel (→ HTTP 503). The tier→behaviour decision lives in the sink impl (#54's DegradingSink), not the service — the service just honours Reserve's verdict. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -140,4 +140,9 @@ type CaptureReceipt struct {
|
||||
Errors []ItemError `json:"errors"`
|
||||
EffectiveClassification string `json:"effective_classification,omitempty"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
// AuditBuffered is true when the central audit sink was unreachable and
|
||||
// this capture's audit record was written to the durable local buffer
|
||||
// instead (internal/public tier). Surfaces the degraded state to the
|
||||
// caller per §4.4.
|
||||
AuditBuffered bool `json:"audit_buffered,omitempty"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user