Documentation
¶
Overview ¶
Package audit writes a structured, post-redaction record for every call through an asynchronous single-writer pipeline, so audit I/O never sits on the request hot path (design §5, §8, ADR-0010).
The hot path hands a record to a bounded buffer with a non-blocking handoff. A dedicated writer goroutine marshals each record to a line of JSON, batches them through a bufio.Writer, and group-commit fsyncs to the sink. Overflow is tiered: high-volume success records are shed once the buffer passes a high-water mark, while security records (denials, redactions) take a hard path that blocks briefly and, if the sink is wedged, fail the request closed so an un-auditable security decision never proceeds.
Record must not be called after Close; the composition root closes the writer only once request handlers have drained (graceful shutdown), so no producer races a Close.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrClosed = errors.New("audit writer closed")
ErrClosed is returned by Record after the writer has been closed.
var ErrUnrecordable = errors.New("audit record unrecordable; sink wedged")
ErrUnrecordable is returned by Record when a security record cannot be buffered before the security block timeout; the caller must fail the request closed (design §5).
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Buffer int
HighWaterMark float64
FlushMaxRecords int
FlushMaxInterval time.Duration
FsyncInterval time.Duration
Overflow OverflowPolicy
SecurityBlockTimeout time.Duration
}
Config tunes the audit pipeline. Zero fields fall back to package defaults.
type OverflowPolicy ¶
type OverflowPolicy string
OverflowPolicy controls what happens to a success record when the buffer is past its high-water mark.
const ( // OverflowShed drops success records past the high-water mark (default), // keeping the handoff non-blocking on the hot path. OverflowShed OverflowPolicy = "shed" // OverflowBlock makes success records wait for buffer room instead of being // shed. It deliberately trades the non-blocking hot-path guarantee for // zero success-record loss, so a wedged sink can back-pressure callers; use // it only when that trade is acceptable (security records are unaffected — // they always take the fail-closed hard path). OverflowBlock OverflowPolicy = "block" )
type SyncWriter ¶
SyncWriter is the audit sink: a writer that can flush durably to its backing store. *os.File satisfies it; use NopSync to wrap stdout.
func NopSync ¶
func NopSync(w io.Writer) SyncWriter
NopSync adapts a plain writer (e.g. os.Stdout, where fsync is not meaningful) to a SyncWriter with a no-op Sync.
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer is the asynchronous audit pipeline. It is safe for concurrent Record calls; a single goroutine owns all I/O.
func New ¶
func New(sink SyncWriter, cfg Config) *Writer
New starts an audit writer feeding the given sink.
func (*Writer) Close ¶
Close drains buffered records, flushes and fsyncs, and stops the writer. It is idempotent. It must be called only after request handlers have stopped submitting records (graceful shutdown drains in-flight calls first).
func (*Writer) Get ¶
func (w *Writer) Get() *domain.AuditRecord
Get returns a cleared record from the pool for the caller to fill. After passing it to Record the caller must not touch it again.