audit

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 3, 2026 License: MPL-2.0 Imports: 22 Imported by: 0

README

Audit Module

This optional Module is part of the existing Foundation Go module on the v0.2 source line. Descriptor.Version reports the actual packaged Foundation dependency through achrix.Version(). The existing ModuleVersion = "0.2.0-development" constant is retained only as a deprecated unpublished source-line label, not component/update/recovery identity; achrix.audit.append, achrix.audit.query and achrix.audit.export use ABI 1 and require Core authorization ABI 2. The published v0.1.0 line is unchanged.

Products explicitly install audit.Migrate(ctx, dsn) with a deadline before startup, then compose audit.NewPostgres(dsn, audit.Config{Now: clock}, logger) and construct audit.NewService(app, module). Runtime startup verifies the exact immutable migration ledger and enabled mutation guards; it does not migrate. This baseline uses ordinary PostgreSQL 18 with UTF8, transactional DDL, native transactions and advisory locks, without extensions. Installation and runtime DSN sources, database roles and secrets remain product-owned.

Identity is the real proving domain. Successful account provision, credential replacement, self password change, account status transition and revoke-all are atomic-required: Identity authorizes its domain capability, Audit Prepare authorizes append on the same stable account target, then Identity changes its owned state and calls audit.AppendInTx within that one native PostgreSQL transaction. Audit owns its SQL; Identity never writes Audit tables. Any append error requires rollback of the entire domain transaction. A successful append return is not commit proof. A lost commit acknowledgement is unavailable, not permission to replay a mutation; the domain owns its revision/reconciliation behavior.

Event carries only bounded action, stable target, owning authority capability and the succeeded outcome. Audit derives actor from the authorized principal, generates the opaque record ID and captures the injected UTC time at microsecond precision. Authority metadata describes the domain contract; it grants no permission. The caller must pass opaque actor/target references and domain classification names, never credentials, password hashes, session secrets, display text or request bodies. Audit has no arbitrary payload field. Successful ordinary login, session rotation/logout and repetitive denied/invalid attempts remain outside the retained dataset; operational diagnostics own those concerns.

Prepared is opaque, single-use and bound to its originating live context/deadline and Module. A fresh context cannot revive a canceled preparation. AppendInTx tracks and cancels its owned SQL during Module stop while the caller retains transaction/commit ownership. The consumer must drain its transaction work before Audit stops; Identity declares that dependency. CheckDatabase compares normalized connection credentials, database, runtime/TLS/protocol and fallback settings without exposing them, rejecting ambiguous driver callback settings conservatively. Remote primary and every fallback require certificate-verified TLS with hostname verification. Unix/loopback plaintext is the local development profile, not a remote deployment support claim. Driver environment/service/password/certificate resolution remains trusted product configuration, not public request input.

Query and export have separate target-specific permissions and limits of 1–100 records per call. Cursors use an internal sequence and are bound to the target; every page reauthorizes. Each page reads committed visible records. Pages are not one consistent snapshot across requests, and concurrent insert transactions can become visible out of sequence. For a complete consistent export/recovery operation, quiesce accountable domain writes or use the product's supported database snapshot/backup profile. The baseline retains all records and provides no deletion, mutation or invented retention/legal policy. Corrections/redaction need a separately accepted domain/product requirement.

The owned schema rejects record UPDATE, DELETE and TRUNCATE with database triggers and constrains identifiers/metadata. This is append-oriented application integrity, not cryptographic tamper evidence: privileged database operators can change/drop the guards or schema. Startup and explicit installation checks verify guard presence; readiness only pings the database; they are not continuous privileged-tampering detection. Restore must preserve the entire owned audit schema, records, sequence, immutable migration ledger and trigger functions, together with the coupled Identity/account state. Source rollback does not reverse retained state. Whole-product/off-host/RPO/RTO support remains downstream of the real product recovery work.

Pool bounds are four connections, no minimum/idle preallocation, one minute idle/health period, one hour lifetime with no jitter, and one second connect/ping limits. Public calls require deadlines; at most 16 owned operations are admitted without queues. Operational failures return safe errors and emit at most one fixed structured record per second per Module, with a fixed counter. Repeated denied/invalid/capacity traffic creates neither Audit records nor provider-error logs.

ACHRIX_AUDIT_TEST_DSN must identify the separate task-owned validation database with the achrix_audit_ name prefix; tests preflight that guard before explicitly resetting only its Audit schema. Identity tests use a different database and install both owned schemas to prove the real same-transaction consumer. The shared validation script additionally owns normal pinned consumption and retained-dataset restore proof.

Documentation

Overview

SPDX-License-Identifier: MPL-2.0 Package audit owns retained accountability records. Domains choose accountable actions; operational logs and repeated public denials are not Audit events.

SPDX-License-Identifier: MPL-2.0

Index

Constants

View Source
const (
	Append = "achrix.audit.append"
	Query  = "achrix.audit.query"
	Export = "achrix.audit.export"
)
View Source
const ModuleVersion = "0.2.0-development"

ModuleVersion is the unpublished source-line label, not the packaged build version. Deprecated: use Module.Descriptor().Version for component/update/recovery identity.

Variables

View Source
var (
	ErrConfiguration = errors.New("invalid audit configuration")
	ErrInput         = errors.New("invalid audit input")
	ErrUnavailable   = errors.New("audit unavailable")
	ErrConflict      = errors.New("audit preparation already consumed")
	ErrLimited       = errors.New("audit capacity exceeded")
)

Functions

func AppendInTx

func AppendInTx(parent context.Context, tx pgx.Tx, prepared Prepared) error

AppendInTx owns Audit SQL while the caller owns the native PostgreSQL transaction and commit. The domain must roll back if this call fails. A successful return alone does not prove either write has committed.

func Migrate

func Migrate(ctx context.Context, dsn string) error

Migrate is explicit installation into the product-owned database. Runtime startup checks identity and never executes migration SQL.

Types

type Config

type Config struct{ Now func() time.Time }

Config.Now is a trusted, concurrent-safe product time source. Records store UTC microsecond instants; it conveys no trusted timestamp or clock attestation.

type Event

type Event struct{ Action, Target, Authority, Outcome string }

Event deliberately has no caller-selected actor, time, ID or arbitrary payload. Authority identifies the owning domain capability, not an authorization grant.

type Module

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

Module owns only Audit storage, pool, admission and lifecycle. Identity's declared dependency orders its shutdown drain before this Module is stopped.

func NewPostgres

func NewPostgres(dsn string, config Config, logger *slog.Logger) (*Module, error)

func (*Module) Descriptor

func (m *Module) Descriptor() achrix.Descriptor

func (*Module) FailureCount

func (m *Module) FailureCount() uint64

FailureCount counts bounded operational failures, not durable accountability.

func (*Module) Ready

func (m *Module) Ready(parent context.Context) error

func (*Module) Start

func (m *Module) Start(ctx context.Context) error

func (*Module) Stop

func (m *Module) Stop(ctx context.Context) error

type Page

type Page struct {
	Records    []Record
	NextCursor string
}

type Prepared

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

Prepared is opaque, single-use and bound to the original live operation and Audit Module. It cannot extend a deadline or be retained as an access grant.

type Record

type Record struct {
	ID         string
	Actor      achrix.Principal
	Action     string
	Target     string
	Authority  string
	Outcome    string
	OccurredAt time.Time
}

type Service

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

func NewService

func NewService(app *achrix.Application, module *Module) (*Service, error)

func (*Service) Append

func (s *Service) Append(parent context.Context, actor achrix.Principal, event Event) (Record, error)

Append commits an authorized standalone record. Atomic domain actions instead use Prepare/AppendInTx within their owning transaction.

func (*Service) CheckDatabase

func (s *Service) CheckDatabase(dsn string) error

CheckDatabase prevents a same-transaction consumer from silently composing different database profiles. Equivalent parsed connection settings match; passwords, TLS trust and fallback settings are compared without being exposed.

func (*Service) Export

func (s *Service) Export(ctx context.Context, actor achrix.Principal, target, cursor string, limit int) (Page, error)

func (*Service) Prepare

func (s *Service) Prepare(ctx context.Context, actor achrix.Principal, event Event) (Prepared, error)

func (*Service) Query

func (s *Service) Query(ctx context.Context, actor achrix.Principal, target, cursor string, limit int) (Page, error)

Query and Export require separate permissions on the exact target. Each is a bounded page of committed visible records, not a snapshot across requests.

Jump to

Keyboard shortcuts

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