Documentation
¶
Overview ¶
Package audit is the admin-action audit trail engine: a bounded in-memory ring (the newest MaxRing entries), optional append-only JSONL persistence with rotation, paginated/time-filtered reads over both, and the Data-Plane → Control-Plane push queue. Extracted from package main's store.go per ADR-0002 (store.go decomposition Phase B).
package main keeps the surfaces: the auditEvent/auditEventDiff request wrappers (actor enrichment from the session cookie), the C2c audit-completion middleware (which observes the wrappers, not this engine), the API handlers, and the CP push loop (which drains/requeues through this package). Two inversion points: the SIEM hook (main wires a closure over its syslog singleton — the forwarder is runtime-configured, so the closure reads it at call time) and the DP-mode flag (set by main's cluster wiring; when on, Add also queues for CP push).
Index ¶
- Constants
- func Add(e Entry)
- func ClearPersistForTest()
- func Close() error
- func DPMode() bool
- func Init(path string) error
- func MaxPendingForTest() int
- func PendingDrops() int64
- func PersistActive() bool
- func QueueForClusterForTest(e Entry)
- func Requeue(events []Entry)
- func ResetForTest() (restore func())
- func ResetPendingForTest() (restore func())
- func ResetWriteErrorsForTest() (restore func())
- func SetDPMode(on bool)
- func SetPersistForTest(w io.Writer) (restore func())
- func SetSIEM(fn func(Entry))
- func SetWriteFailureObserver(fn func(path string, err error))
- func SetWriteSuccessObserver(fn func(path string))
- func SwapRingForTest() (restore func())
- func WriteErrors() int64
- type Entry
Constants ¶
const MaxRing = 500
MaxRing bounds the in-memory ring. Tests MUST NOT assert on len() deltas of Get() — under cumulative suites the ring saturates and adding an entry evicts the oldest (see the CLAUDE.md test-authoring pitfall).
Variables ¶
This section is empty.
Functions ¶
func Add ¶
func Add(e Entry)
Add appends an entry to the in-memory ring and, when configured, to the persistent JSONL file, the SIEM hook, and the DP push queue.
func ClearPersistForTest ¶
func ClearPersistForTest()
ClearPersistForTest drops the persistence wiring without closing it (used after a shutdown-hook test has already closed the file, so a later restore or Close cannot double-close).
func Close ¶
func Close() error
Close releases the persistent file handle (best-effort; shutdown hook). Safe when persistence was never initialised.
func Init ¶
Init opens path for append-only JSONL persistence with rotation. Existing entries are loaded into the in-memory ring on startup. If path is empty this is a no-op (backwards-compatible). F18: Rotates at 50 MB (same as the system log) to prevent unbounded disk growth.
func MaxPendingForTest ¶ added in v1.0.222
func MaxPendingForTest() int
MaxPendingForTest exposes the push-queue cap so a gate can overflow it by an exact amount without duplicating the constant.
func PendingDrops ¶ added in v1.0.222
func PendingDrops() int64
PendingDrops returns the cumulative count of audit entries that never reached the Control Plane because the DP push queue was at its cap (process lifetime; never reset). Non-zero means the CENTRALIZED audit trail has a gap — the local JSONL record on this node is unaffected. Surfaced on /metrics and /healthz.
func PersistActive ¶
func PersistActive() bool
PersistActive reports whether a persistent file handle is wired. Used both by tests (shutdown-hook coverage) and by the admin API (GET /api/stats) to surface a silent Init failure: compare against the caller's own configured path to detect an operator-configured log that fell back to volatile in-memory storage.
func QueueForClusterForTest ¶ added in v1.0.222
func QueueForClusterForTest(e Entry)
QueueForClusterForTest enqueues one entry on the DP→CP push queue directly, without going through Add (which would also write the ring and the JSONL file). Test-only seam for the overflow gates.
func Requeue ¶
func Requeue(events []Entry)
Requeue prepends failed events back into the pending queue so they are retried on the next push interval instead of being lost (newest kept under the cap).
func ResetForTest ¶
func ResetForTest() (restore func())
ResetForTest snapshots and clears the ring + persistence state, returning a restore func. Replaces the pre-extraction pattern of tests swapping the package globals directly.
func ResetPendingForTest ¶ added in v1.0.222
func ResetPendingForTest() (restore func())
ResetPendingForTest snapshots and clears the DP→CP push queue, its drop counter and the one-shot log gate, returning a restore func. Test-only: the production counter is process-lifetime and never reset.
func ResetWriteErrorsForTest ¶ added in v1.0.191
func ResetWriteErrorsForTest() (restore func())
ResetWriteErrorsForTest zeroes the durable-write failure counter, the one-shot log gate and the observer, returning a restore func. Test-only: the production counters are process-lifetime and never reset.
func SetDPMode ¶
func SetDPMode(on bool)
SetDPMode marks this node as a Data Plane (enables CP push queuing).
func SetPersistForTest ¶
SetPersistForTest points JSONL persistence at w (path stays empty so reads keep using the ring), returning a restore func.
func SetSIEM ¶
func SetSIEM(fn func(Entry))
SetSIEM installs the SIEM forwarding hook called after every Add.
func SetWriteFailureObserver ¶ added in v1.0.191
SetWriteFailureObserver publishes the durable-write failure observer. A nil fn clears it (the counter and the one-shot log line still apply), so a mis-wired or un-wired startup can never silence the loss entirely.
func SetWriteSuccessObserver ¶ added in v1.0.191
func SetWriteSuccessObserver(fn func(path string))
SetWriteSuccessObserver publishes the durable-write success observer. A nil fn clears it. Same re-entrancy contract as SetWriteFailureObserver: the observer MUST NOT call Add.
func SwapRingForTest ¶
func SwapRingForTest() (restore func())
SwapRingForTest snapshots and clears ONLY the in-memory ring, returning a restore func (persistence state untouched).
func WriteErrors ¶ added in v1.0.191
func WriteErrors() int64
WriteErrors returns the cumulative count of audit entries that did NOT reach the persistent JSONL file (process lifetime; never reset). Non-zero means the durable audit trail is incomplete — surfaced on GET /api/stats, /healthz and /metrics so the gap is never silent.
Types ¶
type Entry ¶
type Entry struct {
TS int64 `json:"ts"` // Unix milliseconds
Time string `json:"time"` // human-readable "2006-01-02 15:04:05"
Actor string `json:"actor"` // client IP (or authenticated username)
Action string `json:"action"` // "policy.add" | "blocklist.remove" | …
Object string `json:"object"` // the specific item that changed (human-readable name)
ObjectID string `json:"objectId,omitempty"` // stable ULID of the changed item, when it has one — survives rename, so an object's audit trail is correlatable by ID (§1 identity seam)
Detail string `json:"detail"` // extra context (never contains credentials)
Before string `json:"before,omitempty"` // JSON snapshot before the change
After string `json:"after,omitempty"` // JSON snapshot after the change
}
Entry captures every configuration change made through the UI/API so operators can answer "Who changed What, and When?" — a core SOC requirement. Actor is the client IP of the UI caller, enriched with the authenticated admin identity when available. Action follows a "resource.verb" naming scheme (e.g. "policy.add").
func GetMemory ¶
GetMemory returns paginated, optionally time-filtered entries from the in-memory ring (newest-first).
func GetPersistent ¶
GetPersistent reads the JSONL audit log file with pagination. Returns entries newest-first. If from/to are non-zero, filters by timestamp. Falls back to the in-memory ring if no file is configured.