audit

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package audit records an append-only trail of destructive operations (backup, restore, sync, prune) — who ran what, against which profile, when, and whether it succeeded. It is a stdlib-only leaf: the app layer holds an Auditor and calls Begin before a destructive verb and End after it.

The same Begin/End seam is the interception point reused by other cross- cutting concerns (2FA gating runs as a pre-check before the verb; telemetry records timing/outcome) — they are layered on top rather than each re-wrapping every verb.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Record

func Record(ctx context.Context, a Auditor, ev Event) func(error)

Record runs Begin, returns a function to defer that calls End with the verb's error. Usage in an app verb:

done := audit.Record(ctx, d.Auditor, audit.Event{Op: audit.OpPrune, Profile: p, Actor: actor})
defer func() { done(retErr) }()

A nil auditor yields a no-op done func, so callers need no nil check.

Types

type Auditor

type Auditor interface {
	// Begin records the start of op and returns a handle to End. Implementations
	// may persist a "started" record or defer all writes to End; the app layer
	// does not care which.
	Begin(ctx context.Context, ev Event) Handle
}

Auditor records audit events. A nil Auditor is valid and is a no-op, so the app layer can call Begin/End unconditionally without nil checks.

func NewMulti

func NewMulti(auditors ...Auditor) Auditor

NewMulti returns an Auditor that broadcasts to all non-nil auditors, or nil if none are live (so callers keep the nil-is-no-op contract).

type Event

type Event struct {
	Time       time.Time `json:"time"`
	Op         Op        `json:"op"`
	Profile    string    `json:"profile,omitempty"`
	Target     string    `json:"target,omitempty"` // e.g. sync destination, dump id
	Actor      string    `json:"actor"`            // OS user who ran the command
	Outcome    string    `json:"outcome"`          // "ok" | "error" (set at End)
	Err        string    `json:"error,omitempty"`  // error text when Outcome == "error"
	DurationMS int64     `json:"duration_ms"`      // wall time Begin→End
}

Event is one audit record. Outcome and DurationMS are filled in at End; the rest are known at Begin.

type FileAuditor

type FileAuditor struct {
	// contains filtered or unexported fields
}

FileAuditor appends audit events as JSONL to a single log file. Writes are serialized by a mutex so concurrent verbs (e.g. parallel jobs) don't interleave partial lines. A write error is swallowed: auditing must never fail the operation it records (a backup that succeeded must not report failure because the audit line couldn't be written) — best-effort, append-only.

func NewFileAuditor

func NewFileAuditor(path string, now func() time.Time) (*FileAuditor, error)

NewFileAuditor returns an Auditor appending to path, creating its parent directory. now defaults to time.Now when nil.

func (*FileAuditor) Begin

func (a *FileAuditor) Begin(_ context.Context, ev Event) Handle

type Handle

type Handle interface {
	// End records the operation's outcome. err == nil means success. It is safe
	// to call End on a zero/nil Handle (no-op).
	End(err error)
}

Handle finalizes one operation's audit record.

type Multi

type Multi struct {
	// contains filtered or unexported fields
}

Multi fans one operation's Begin/End out to several Auditors, so the same destructive-op seam feeds both the security audit log and the telemetry recorder without the app layer wiring two hooks. Nil entries are skipped, and Multi of zero live auditors is itself a no-op (returns nil from New).

func (*Multi) Begin

func (m *Multi) Begin(ctx context.Context, ev Event) Handle

type Op

type Op string

Op names a destructive operation, used as the audit record's action.

const (
	OpBackup  Op = "backup"
	OpRestore Op = "restore"
	OpSync    Op = "sync"
	OpPrune   Op = "prune"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL