Help us shape the future of Go! Take the 2026 Go Developer Survey

audit

package
v0.5.3 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package audit records model history and typed domain events through the actual business transaction. Registration is explicit and never migrates storage.

Index

Constants

View Source
const (
	DefaultMaxValueBytes = 64 << 10
	ValueBytesFloor      = 256
	ValueBytesCeiling    = record.MaxCapturedValueBytes
)

DefaultMaxValueBytes is the per-value threshold used when Config leaves MaxValueBytes zero. ValueBytesFloor and ValueBytesCeiling bound explicit settings.

View Source
const (
	MigrationOrigin migrate.Origin  = "foundry.audit"
	CreateEntries   migrate.ID      = "000001_create_entries"
	AddHistoryKeys  migrate.ID      = "000002_add_history_keys"
	Introduced      migrate.Version = "v0.1.0"
)

Variables

This section is empty.

Functions

func CorrelationFromContext

func CorrelationFromContext(ctx context.Context) value.Optional[Correlation]

CorrelationFromContext returns the correlation audit writes in ctx will store.

func FindModel

func FindModel[M, K any](ctx context.Context, executor database.Executor, recorder *Recorder, template model.Reference[M, K], id ModelID[M]) (value.Optional[ModelRecord[M, K]], error)

FindModel reads an audit-row ID owned by the expected model. Supply a generated zero model's FoundryReference as the decoding template; its current key is not used. Foreign models/areas are absent, and no subject-model lookup occurs.

func Migrations

func Migrations() []migrate.Definition

Migrations returns owned historical definitions. Register and run them with the ordinary explicit migration command against the recorder's database/schema. Missing storage fails enabled audit writes instead of silently discarding them.

AddHistoryKeys rewrites the table once: it assigns every existing row an insertion sequence in (created_at, id) order, records the original redaction policy, backfills request correlation and adds database-generated subject and actor lookup keys with their history indexes. The lookup keys hash UTF-8 text, so the migration requires a UTF8 (or SQL_ASCII) database encoding.

func ModelHistory

func ModelHistory[M, K any](ctx context.Context, executor database.Executor, recorder *Recorder, subject model.Reference[M, K], request orm.PageRequest) (orm.PageResult[ModelRecord[M, K]], error)

ModelHistory reads one stored subject's lifecycle history in the selected area, newest first by insertion sequence. It does not fetch the subject model or require that it still exists. Page count and rows share ORM pagination's transaction/isolation contract; use ModelTimeline for keyset pages.

func Register

func Register(r *foundation.Registrar, pool foundation.Key[*database.DB], key foundation.Key[*Recorder], config Config, models ...record.Declaration) error

Register constructs one recorder and adds generated model audit observers to the selected pool. Foundation owns duplicate detection, dependency resolution and freezing; model callbacks retain the constructed recorder directly.

func WithArea

func WithArea(ctx context.Context, area Area) (context.Context, error)

WithArea scopes subsequent audit writes/reads while retaining cancellation and attribution. It does not authenticate the caller or grant history access.

func WithCorrelation

func WithCorrelation(ctx context.Context, correlation Correlation) (context.Context, error)

WithCorrelation scopes subsequent audit writes to an explicit correlation, such as one batch of a bulk operation. Without it, rows use the attribution request ID when one is present, so a request's changes can be read together.

Types

type Action

type Action[P any] struct {
	// contains filtered or unexported fields
}

Action declares a versioned domain audit DTO. Declare and reuse one descriptor for each schema. Redaction never changes the input or silently hydrates a partially redacted DTO on subsequent reads.

func Define

func Define[P any](name ActionName, version Version) Action[P]

Define returns an action descriptor for the payload type, name and version. Validation is deferred to Validate or use of the descriptor.

func (Action[P]) Find

func (a Action[P]) Find(ctx context.Context, executor database.Executor, recorder *Recorder, id ActionID[P]) (value.Optional[ActionRecord[P]], error)

Find selects only this action schema and area. It validates persisted DTO and redaction metadata again under the row's own redaction policy; a foreign action/version/area is absent.

func (Action[P]) Name

func (a Action[P]) Name() ActionName

Name returns the declared action name.

func (Action[P]) Record

func (a Action[P]) Record(ctx context.Context, tx *database.Tx, recorder *Recorder, payload P) (ActionID[P], error)

Record captures a domain event without a model subject. It neither commits the outer transaction nor delivers a notification. Its ID is not a receipt.

func (Action[P]) Timeline

func (a Action[P]) Timeline(ctx context.Context, executor database.Executor, recorder *Recorder, request TimelineRequest) (Timeline[ActionRecord[P]], error)

Timeline lists this action name/version in the selected area, newest first.

func (Action[P]) Validate

func (a Action[P]) Validate() error

Validate reports fault.Invalid for an invalid name, zero version or interface payload type.

func (Action[P]) Version

func (a Action[P]) Version() Version

Version returns the declared schema version.

type ActionEntry

type ActionEntry[P any] struct {
	// contains filtered or unexported fields
}

ActionEntry distinguishes audit-row IDs by their action payload type.

type ActionID

type ActionID[P any] = model.ID[ActionEntry[P]]

ActionID identifies an audit row for payload type P.

func ActionActivityID

func ActionActivityID[P any](activity Activity, action Action[P]) (ActionID[P], bool)

ActionActivityID converts a domain activity into its typed ID when the row was recorded by this action name and version.

func ParseActionID

func ParseActionID[P any](text string) (ActionID[P], error)

ParseActionID parses the textual audit-row identifier for payload type P.

func RecordFor

func RecordFor[M, K, P any](ctx context.Context, tx *database.Tx, recorder *Recorder, action Action[P], subject model.Reference[M, K], payload P) (ActionID[P], error)

RecordFor captures a concrete model-owned subject reference without another model lookup or read accessor. Origin/subject metadata is not authorization.

type ActionName

type ActionName string

ActionName identifies a domain audit schema.

type ActionRecord

type ActionRecord[P any] struct {
	Metadata
	// contains filtered or unexported fields
}

ActionRecord is immutable persisted domain history. Document.Decode returns a fresh DTO only when the stored representation is complete and unredacted. Its embedded Metadata supplies Sequence, Area, Origin, Correlation, Route and CreatedAt.

func (ActionRecord[P]) Document

func (r ActionRecord[P]) Document() record.Document[P]

Document returns the captured audit representation of the payload.

func (ActionRecord[P]) Format

func (ActionRecord[P]) Format(state fmt.State, _ rune)

Format writes a fixed label without exposing the record's contents.

func (ActionRecord[P]) ID

func (r ActionRecord[P]) ID() ActionID[P]

ID returns the typed audit-row identifier.

func (ActionRecord[P]) Subject

func (r ActionRecord[P]) Subject() value.Nullable[model.Identity]

Subject returns the recorded subject identity, or null for an action without a subject.

type Activity

type Activity struct {
	Metadata
	// contains filtered or unexported fields
}

Activity is a payload-free summary of one stored audit row for heterogeneous timelines: a subject's combined history, an actor's changes or everything written under one correlation. Load typed details with FindModel/Action.Find using ModelActivityID or ActionActivityID. Its embedded Metadata supplies Sequence, Area, Origin, Correlation, Route and CreatedAt.

func (Activity) Action

func (a Activity) Action() (ActionName, Version)

Action returns the domain action name and version; both are zero for model rows.

func (Activity) Format

func (Activity) Format(state fmt.State, _ rune)

Format writes a fixed label without exposing activity metadata.

func (Activity) IsAction

func (a Activity) IsAction() bool

IsAction reports whether the lifecycle operation is zero, as it is for domain actions.

func (Activity) Operation

func (a Activity) Operation() lifecycle.Operation

Operation is the model lifecycle operation, or zero for a domain action.

func (Activity) Subject

func (a Activity) Subject() value.Nullable[model.Identity]

Subject returns the recorded subject identity, or null when none was captured.

type Area

type Area string

Area identifies an application audit namespace. It is attribution metadata, not an authorization boundary; applications authorize access to history.

func (Area) Validate

func (a Area) Validate() error

Validate reports fault.Invalid unless the area is a semantic identifier.

type Config

type Config struct {
	Area          Area
	RetentionDays uint16
	MaxValueBytes int
}

Config is immutable after recorder construction. Zero RetentionDays disables configured pruning. Nothing runs retention automatically. A captured value larger than MaxValueBytes (zero selects DefaultMaxValueBytes) is stored as a size and SHA-256 digest marker instead of failing the business write.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the application area and default per-value byte limit.

func (Config) Validate

func (c Config) Validate() error

Validate checks the area and explicit value limit, returning fault.Invalid for invalid settings.

type Correlation

type Correlation string

Correlation groups audit rows written by one logical operation, such as a request, import or bulk change. It is metadata for reading history together, not a secret, authorization scope or idempotency key.

func NewCorrelation

func NewCorrelation() (Correlation, error)

NewCorrelation generates a time-ordered UUIDv7 correlation identifier.

func (Correlation) Validate

func (c Correlation) Validate() error

Validate applies the attribution request-ID bounds: 1–128 bytes of valid, trimmed UTF-8 without control characters.

type Metadata added in v0.3.0

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

Metadata contains the provenance shared by ModelRecord, ActionRecord and Activity. Audit history readers populate it from validated stored rows. These records embed Metadata to promote Sequence, Area, Origin, Correlation, Route and CreatedAt. Its zero value describes no stored row: accessors return zero values and absent optional values.

func (Metadata) Area added in v0.3.0

func (m Metadata) Area() Area

Area returns the audit namespace in which the row was recorded.

func (Metadata) Correlation added in v0.3.0

func (m Metadata) Correlation() value.Optional[Correlation]

Correlation returns the logical operation identifier captured for the row, if set.

func (Metadata) CreatedAt added in v0.3.0

func (m Metadata) CreatedAt() temporal.DateTime

CreatedAt returns the timestamp captured when the row was written.

func (Metadata) Origin added in v0.3.0

func (m Metadata) Origin() attribution.Origin

Origin returns the attribution captured when the row was written.

func (Metadata) Route added in v0.3.0

Route returns the matched request route that wrote the row, when the write happened inside a routed request.

func (Metadata) Sequence added in v0.3.0

func (m Metadata) Sequence() Sequence

Sequence returns the database-assigned insertion position of the row.

type ModelEntry

type ModelEntry[M any] struct {
	// contains filtered or unexported fields
}

ModelEntry owns an audit-row ID for one model; it is not a stored model row.

type ModelID

type ModelID[M any] = model.ID[ModelEntry[M]]

ModelID identifies an audit row for model M.

func ModelActivityID

func ModelActivityID[M, K any](activity Activity, template model.Reference[M, K]) (ModelID[M], bool)

ModelActivityID converts a model activity into its typed audit ID when the row belongs to template's model. It performs no lookup or authorization.

func ParseModelID

func ParseModelID[M any](text string) (ModelID[M], error)

ParseModelID parses the textual audit-row identifier for model M.

type ModelRecord

type ModelRecord[M, K any] struct {
	Metadata
	// contains filtered or unexported fields
}

ModelRecord contains complete audit representations, not a partial model. Its embedded Metadata supplies Sequence, Area, Origin, Correlation, Route and CreatedAt.

func (ModelRecord[M, K]) Changes

func (r ModelRecord[M, K]) Changes() record.Model[M, K]

Changes returns the captured model audit representation.

func (ModelRecord[M, K]) Format

func (ModelRecord[M, K]) Format(state fmt.State, _ rune)

Format writes a fixed label without exposing captured changes.

func (ModelRecord[M, K]) ID

func (r ModelRecord[M, K]) ID() ModelID[M]

ID returns the typed audit-row identifier.

type Recorder

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

Recorder has no independent resource lifetime or database pool. Its methods use caller-owned executors/transactions and are safe for concurrent operations.

func New

func New(config Config) (*Recorder, error)

New validates config and returns a recorder that retains its value copy.

func (*Recorder) CountBefore

func (r *Recorder) CountBefore(ctx context.Context, tx *database.Tx, cutoff temporal.DateTime) (int64, error)

CountBefore counts the entries of the selected area created before cutoff, the rows PruneBefore would remove, without reading their payloads.

func (*Recorder) Format

func (*Recorder) Format(state fmt.State, _ rune)

Format writes a fixed label without exposing recorder configuration.

func (*Recorder) PruneBefore

func (r *Recorder) PruneBefore(ctx context.Context, tx *database.Tx, cutoff temporal.DateTime, limit int) (int64, error)

PruneBefore removes at most limit old entries in the selected area through the supplied transaction. Call explicitly from an authorized maintenance operation. It does not reset tables, hydrate payloads, dispatch observers or loop forever.

func (*Recorder) PruneRetention

func (r *Recorder) PruneRetention(ctx context.Context, tx *database.Tx, now temporal.DateTime, limit int) (int64, error)

PruneRetention derives the explicit cutoff from supplied UTC time. Each day is 24 hours. Zero configured retention disables this helper; no background work or default deletion schedule is installed by constructing/registering a recorder.

func (*Recorder) RecordModel

func (r *Recorder) RecordModel(ctx context.Context, tx *database.Tx, entry record.Entry) error

RecordModel implements the generated observer boundary. Captured stored changes are validated before any SQL. Values above the configured MaxValueBytes are stored as digest markers; a large payload never fails the write. Other failures roll back the model write.

func (*Recorder) RetentionCutoff

func (r *Recorder) RetentionCutoff(now temporal.DateTime) (temporal.DateTime, error)

RetentionCutoff is now minus the configured retention in 24-hour days. Zero retention returns Missing; a zero now is invalid.

type Scope

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

Scope is a concrete, immutable dependency for domain constructors. It borrows one recorder and database; callers may retain it until application shutdown. It does not retain a service resolver or acquire another pool.

func NewScope

func NewScope(db *database.DB, schema string, recorder *Recorder) (*Scope, error)

NewScope validates and binds a borrowed database and recorder to the audit schema. It acquires no resources and performs no database I/O.

func (*Scope) CountBefore

func (s *Scope) CountBefore(ctx context.Context, cutoff temporal.DateTime) (int64, error)

CountBefore counts, in one read-only transaction, the entries PruneBefore would remove from the selected area. It never reads payloads or deletes.

func (*Scope) Format

func (*Scope) Format(state fmt.State, _ rune)

Format writes a fixed label without exposing the scope's dependencies.

func (*Scope) LogValue

func (*Scope) LogValue() slog.Value

LogValue returns a fixed label without exposing the scope's dependencies.

func (*Scope) PruneBefore

func (s *Scope) PruneBefore(ctx context.Context, cutoff temporal.DateTime, batch int) (int64, error)

PruneBefore repeatedly removes bounded batches of entries older than cutoff from the selected area, each batch in its own transaction, until a batch removes fewer than batch rows or ctx ends. It returns the rows removed by committed batches, including when a later batch fails; rerunning is safe. Call it only from an explicit, authorized maintenance operation.

func (*Scope) PruneRetention

func (s *Scope) PruneRetention(ctx context.Context, now temporal.DateTime, batch int) (int64, error)

PruneRetention derives one cutoff from now and the configured retention, then prunes in bounded batches like PruneBefore. Zero retention returns Missing.

func (*Scope) RetentionCutoff

func (s *Scope) RetentionCutoff(now temporal.DateTime) (temporal.DateTime, error)

RetentionCutoff derives the configured retention cutoff for now, like Recorder.RetentionCutoff.

func (*Scope) Within

func (s *Scope) Within(ctx context.Context, tx *database.Tx, fn func(*database.Tx, *Recorder) error) error

Within joins the caller's business transaction through a savepoint. All audit work must use child; the configured schema is restored afterward. Success does not commit the caller's transaction, and another database's transaction fails.

type Sequence

type Sequence int64

Sequence is the database-assigned insertion order of audit rows. It orders history correctly even when several rows share one transaction timestamp. It is a position for keyset reads, not an identifier or a count.

type Timeline

type Timeline[R any] struct {
	Items []R
	Next  value.Optional[Sequence]
}

Timeline is one keyset page. Next is set when older rows may follow; it is a position hint, not a snapshot across concurrent writes.

func ActionTimelineFor

func ActionTimelineFor[M, K, P any](ctx context.Context, executor database.Executor, recorder *Recorder, action Action[P], subject model.Reference[M, K], request TimelineRequest) (Timeline[ActionRecord[P]], error)

ActionTimelineFor lists this action's records about one subject, newest first, using the indexed subject key and an exact identity comparison.

func ActorActivity

func ActorActivity[M, K any](ctx context.Context, executor database.Executor, recorder *Recorder, actor model.Reference[M, K], request TimelineRequest) (Timeline[Activity], error)

ActorActivity lists rows whose attribution names this model actor, newest first. The actor key is indexed; each row's origin is also compared exactly.

func CorrelationActivity

func CorrelationActivity(ctx context.Context, executor database.Executor, recorder *Recorder, correlation Correlation, request TimelineRequest) (Timeline[Activity], error)

CorrelationActivity lists every row written under one correlation, such as one request or explicit batch, newest first.

func ModelTimeline

func ModelTimeline[M, K any](ctx context.Context, executor database.Executor, recorder *Recorder, subject model.Reference[M, K], request TimelineRequest) (Timeline[ModelRecord[M, K]], error)

ModelTimeline reads one subject's lifecycle history newest first using the indexed insertion sequence. It performs one bounded query without a count; pass Next as Before to continue. Concurrent writes do not form a snapshot.

func SubjectActivity

func SubjectActivity[M, K any](ctx context.Context, executor database.Executor, recorder *Recorder, subject model.Reference[M, K], request TimelineRequest) (Timeline[Activity], error)

SubjectActivity lists model changes and domain actions recorded for one subject, newest first, without decoding their payloads.

func SystemActivity

func SystemActivity(ctx context.Context, executor database.Executor, recorder *Recorder, system attribution.SystemID, request TimelineRequest) (Timeline[Activity], error)

SystemActivity lists rows attributed to one system identity, newest first.

type TimelineRequest

type TimelineRequest struct {
	Size   int
	Before value.Optional[Sequence]
}

TimelineRequest selects at most Size rows, newest first, strictly older than Before when it is set. Pass a returned Timeline.Next as Before to continue.

func (TimelineRequest) Validate

func (r TimelineRequest) Validate() error

Validate checks the page size and optional positive sequence, returning fault.Invalid on failure.

type Version

type Version uint32

Version identifies a revision of a domain audit schema.

Directories

Path Synopsis
Package command supplies the explicit audit retention operation.
Package command supplies the explicit audit retention operation.
Package record supplies typed audit snapshots and generated registration contracts below audit storage.
Package record supplies typed audit snapshots and generated registration contracts below audit storage.

Jump to

Keyboard shortcuts

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