Emits the request-level audit record to structured logs (scraped by the existing alloy/loki substrate) and surfaces security events at warn level. Placeholder behind the AuditSink interface — the classification- aware loki+buffer+reconcile sink (confidential fails closed, internal degrades) lands in #54 and replaces this without touching callers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
49 lines
1.6 KiB
Go
49 lines
1.6 KiB
Go
// Package audit provides AuditSink implementations for the capture
|
|
// capability (I5). This file ships the minimal slog-backed sink used in
|
|
// #53: it emits the request-level audit record to structured logs, which
|
|
// the alloy/loki substrate already scrapes. The classification-aware
|
|
// degradation/refusal sink (confidential fails closed, internal buffers +
|
|
// reconciles) lands in #54 and replaces this behind the same interface.
|
|
package audit
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/capture"
|
|
)
|
|
|
|
// SlogSink records audit entries to an slog.Logger. It never fails, so it
|
|
// does not exercise the I5 floor (refuse-if-unauditable) — that is #54's
|
|
// loki+buffer sink. A nil logger falls back to slog.Default().
|
|
type SlogSink struct {
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// NewSlogSink constructs a SlogSink. nil logger ⇒ slog.Default().
|
|
func NewSlogSink(logger *slog.Logger) *SlogSink {
|
|
if logger == nil {
|
|
logger = slog.Default()
|
|
}
|
|
return &SlogSink{logger: logger}
|
|
}
|
|
|
|
// Record emits the audit entry at info level. Security events, when
|
|
// present, are logged at warn level so they surface independently of the
|
|
// routine audit stream.
|
|
func (s *SlogSink) Record(_ context.Context, e capture.AuditEntry) error {
|
|
s.logger.Info("capture audit",
|
|
"principal", e.Principal,
|
|
"actor", e.Actor,
|
|
"harness", e.Harness,
|
|
"session_ref", e.SessionRef,
|
|
"classification", e.EffectiveClassification,
|
|
"items", e.Items,
|
|
"ts", e.Timestamp,
|
|
)
|
|
for _, ev := range e.SecurityEvents {
|
|
s.logger.Warn("capture security event", "principal", e.Principal, "event", ev)
|
|
}
|
|
return nil
|
|
}
|