audit

package
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package audit defines an append-and-prune store for projected device and administrative events.

Design

Records retain metadata, actor strings and fields selected by server/eventsink. The interface supports append, filtered cursor queries and age-based pruning, with no update or delete-by-ID operation. Backends live in server/audit/inmem and server/audit/sqlstore and share the audittest suite.

The interface does not make the database tamper-evident. Persistence is optional, write failures do not roll back device operations, and asynchronous events can be lost on abrupt shutdown. Configure durable storage, access controls, retention and backups when records must survive process failure.

References

Index

Constants

View Source
const DefaultPageSize = 100

DefaultPageSize applies when Page.Limit is not positive.

View Source
const MaxPageSize = 1000

MaxPageSize bounds one page. A caller asking for more gets this many, so a cursor is always cheap to serve.

Variables

View Source
var (
	// ErrNotFound is a record that does not exist.
	ErrNotFound = errors.New("audit: not found")
	// ErrInvalid is a malformed argument: a bad cursor, an empty record, a
	// nil database.
	ErrInvalid = errors.New("audit: invalid argument")
)

Sentinel errors. They mirror the shapes storage and adminauth use so a handler can map them to a status without knowing which store answered.

Functions

This section is empty.

Types

type Page

type Page struct {
	Cursor string
	Limit  int
}

Page requests one page of records. An empty Cursor starts from the newest; Limit <= 0 uses DefaultPageSize.

func (Page) Size

func (p Page) Size() int

Size returns the page size to use: the requested limit, defaulted and bounded, so every backend agrees without repeating the rule.

type Query

type Query struct {
	// Type restricts to one event type.
	Type string
	// Actor restricts to one actor, which is how "what did this admin do"
	// and "what did break-glass do" are asked.
	Actor string
	// Enrollment restricts to one enrollment id.
	Enrollment string
	// Since and Until bound the record time. Zero means unbounded.
	Since, Until time.Time
}

Query filters a listing. The zero Query matches everything.

func (Query) Matches

func (q Query) Matches(rec Record) bool

Matches reports whether rec satisfies q. The in-memory store filters with it; the SQL stores build the equivalent WHERE clause, and the contract suite is what keeps the two agreeing.

type Record

type Record struct {
	// EventID identifies the source occurrence. Nonempty IDs are deduplicated
	// by Append, independently of the numeric audit cursor.
	EventID string
	// ID orders the trail and is the pagination cursor. It is assigned by
	// the store, ascending, so the newest record has the highest id.
	ID int64
	// At is when the event happened, from the publisher's clock.
	At time.Time
	// Type is the event type, for example "command-queued".
	Type string
	// Actor is who caused it: "device", "admin", a principal name, or
	// "break-glass".
	Actor string
	// Enrollment is the enrollment the event concerned, zero for events with
	// no enrollment such as an admin action.
	Enrollment mdm.EnrollmentID
	// Fields is the projected payload, stored as JSON.
	Fields map[string]any
}

Record is one persisted event. It is the projection from event/sink after it has been reduced to what may leave the process, never the raw payload: a TokenUpdate's payload carries the device unlock token, so the trail stores what the projection allowed and nothing else.

type Result

type Result[T any] struct {
	Items      []T
	NextCursor string
}

Result is one page of records with the cursor for the next page ("" at the end).

type Store

type Store interface {
	// Append writes one record and returns it with its assigned ID.
	// ErrInvalid for a record with no type.
	Append(ctx context.Context, rec Record) (Record, error)
	// List pages the trail newest first, filtered by q. An unparsable
	// cursor is ErrInvalid.
	List(ctx context.Context, q Query, p Page) (Result[Record], error)
	// Get returns one record. ErrNotFound when it does not exist.
	Get(ctx context.Context, id int64) (Record, error)
	// Prune removes records older than before and returns how many went.
	// Retention is the only way a record leaves the trail.
	Prune(ctx context.Context, before time.Time) (int, error)
}

Store appends projected audit records, queries them and prunes by age. It exposes no update or delete-by-ID method. Database access controls remain necessary because this API does not prevent direct row modification.

Directories

Path Synopsis
Package audittest defines audit-store contracts and controlled failure injection.
Package audittest defines audit-store contracts and controlled failure injection.
Package inmem provides an in-memory audit trail for tests and development.
Package inmem provides an in-memory audit trail for tests and development.
Package sqlstore implements persistent audit storage for SQLite, PostgreSQL and MySQL.
Package sqlstore implements persistent audit storage for SQLite, PostgreSQL and MySQL.

Jump to

Keyboard shortcuts

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