eventlog

package
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package eventlog wraps the audit log for forensic and compliance use cases (Task 20119): tail (with optional follow), filter, replay into a fresh database, and verify the SHA-256 hash chain.

The append-only `audit_events` table itself lives in pkg/statedb so that mutation hot-paths (UpsertTask, AppendStep, SetConfigBlob, SaveState) can emit rows without an import cycle. This package supplies the workdir-aware API and operator-facing workflows on top.

The audit log is best-effort by design: a stuck DB cannot block user work. `cloop events verify` is the explicit check that the chain is intact; detected breaks tell operators the journal lost or had rows tampered with.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoProject = errors.New("eventlog: no .cloop/state.db found at workdir")

ErrNoProject is returned when the resolved workdir has no SQLite state DB yet — usually because cloop init has not been run.

Functions

This section is empty.

Types

type AuditEvent

type AuditEvent = statedb.AuditEvent

AuditEvent re-exports statedb.AuditEvent so callers don't need to import both packages.

type AuditFilter

type AuditFilter = statedb.AuditFilter

AuditFilter re-exports statedb.AuditFilter.

type Log

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

Log is a handle to an audit log. Concurrency: safe for use by multiple goroutines as long as Close is only called once.

func Open

func Open(workDir string) (*Log, error)

Open opens a handle to the audit log at the resolved state.db path. The caller must Close.

func (*Log) Append

func (l *Log) Append(ev *AuditEvent) error

Append records one event. Caller-supplied actor/event_type/etc. flow through to the row; the package fills timestamp + hash chain.

func (*Log) Close

func (l *Log) Close() error

Close releases the underlying connection.

func (*Log) DB

func (l *Log) DB() *statedb.DB

DB returns the underlying *statedb.DB. Exposed so callers that already need other statedb operations against the same database don't open a second connection.

func (*Log) DistinctActors

func (l *Log) DistinctActors() ([]string, error)

DistinctActors returns the sorted list of actor values seen in the log. Used by the Web UI's filter dropdown.

func (*Log) DistinctEntityTypes

func (l *Log) DistinctEntityTypes() ([]string, error)

DistinctEntityTypes returns the sorted list of entity types seen.

func (*Log) List

func (l *Log) List(f AuditFilter) ([]AuditEvent, int, error)

List returns events matching the filter and the unfiltered total count.

func (*Log) MaxID

func (l *Log) MaxID() (int64, error)

MaxID returns the largest id currently in the audit log; 0 when empty.

func (*Log) Path

func (l *Log) Path() string

Path returns the on-disk path of the SQLite database.

func (*Log) ReconstructTask added in v0.0.2

func (l *Log) ReconstructTask(taskID int) (TaskStory, error)

ReconstructTask assembles one task's whole history — every execution, the executor each ran on, the credentials it held and how it ended — from the audit trail alone (Task 20282).

It reads no table but audit_events. See pkg/statedb/audit_story.go for why that restriction is the feature rather than an implementation detail.

func (*Log) Tail

func (l *Log) Tail(ctx context.Context, opts TailOptions, out chan<- AuditEvent) error

Tail streams events to out in id-ascending order. When Follow is false the function returns once it has caught up to MaxID at the time of the call. When Follow is true the function only returns on context cancellation.

The channel out is closed before Tail returns (success or error). The caller may pass an unbuffered channel; Tail respects ctx during sends.

func (*Log) Verify

func (l *Log) Verify() (VerifyReport, error)

Verify recomputes the SHA-256 chain from the genesis row to head. Returns (report, nil) for a clean verification, including reports where OK=false: the inner Reason describes the break. The error return is reserved for driver/IO failures.

type ReplayOptions

type ReplayOptions struct {
	FromID  int64               // 0 or 1 = entire history; >1 = only events with id >= FromID
	OnEvent func(ev AuditEvent) // optional per-event callback (e.g. progress)
	StopAt  int64               // 0 = no upper bound; otherwise stop after id == StopAt

	// AllowTruncated permits a replay that starts after a retention prune
	// (Task 20218).
	//
	// Replay rebuilds state by applying every mutation in order, so it is only
	// complete if it starts at the first one. Once a prefix has been pruned,
	// the rows before the boundary live in a sealed archive and not in this
	// database — a replay from "the beginning" would quietly produce a partial
	// database that looks whole. Refusing is the safe default; this opts into
	// the partial rebuild deliberately, which is the right call when the tail
	// is all the operator wanted.
	AllowTruncated bool
}

ReplayOptions controls Replay behaviour.

type ReplayReport

type ReplayReport struct {
	DestPath     string
	EventsRead   int
	TasksWritten int
	StepsWritten int
	ConfigWrites int
	Skipped      int
	BreakAtID    int64 // first id whose payload could not be applied (0 = none)
	BreakReason  string
	StartedAt    time.Time
	FinishedAt   time.Time

	// Gaps counts the audit.gap rows replayed past, and GapEvents the events
	// they record as never appended (Task 20404). Mutations among those are
	// in no row, so a rebuild across a gap can be missing them.
	Gaps      int
	GapEvents int64

	// Truncated reports that a retention prune removed events older than the
	// replay's starting point, so the rebuild is of the surviving tail rather
	// than of the whole history. Set only when the caller passed
	// AllowTruncated; without it a truncated trail is an error.
	Truncated          bool
	TruncatedThroughID int64
	ArchivePath        string
}

ReplayReport summarises what Replay applied.

func Replay

func Replay(ctx context.Context, srcWorkDir, destPath string, opts ReplayOptions) (*ReplayReport, error)

Replay reads audit events from the source workdir and applies them to a fresh SQLite database at destPath. The destination is created if missing; if it already exists Replay returns an error rather than overwriting — the operator should remove or rename it first.

Replay disables audit emission on the destination DB so the rebuild does not append a second copy of the events to the new audit log. The hash chain in the destination therefore restarts empty; if the operator wants to preserve the chain they can copy the audit_events table separately.

type TailOptions

type TailOptions struct {
	FromID    int64         // start streaming events with id >= FromID (1 = beginning)
	Follow    bool          // when true, keep polling for new events; when false, return at head
	Poll      time.Duration // follow-mode polling interval (default 500ms)
	Filter    AuditFilter   // additional filtering (Order/Limit/Offset/FromID are overridden)
	BatchSize int           // events per query (default 200, capped at 5000)
}

TailOptions controls Tail behaviour.

type TaskRunStory added in v0.0.2

type TaskRunStory = statedb.TaskRunStory

TaskRunStory is one execution within a TaskStory.

type TaskStory added in v0.0.2

type TaskStory = statedb.TaskStory

TaskStory and TaskRunStory are one task's reconstructed history and one of its executions. Aliased here so callers of this package do not have to import pkg/statedb to name what ReconstructTask returns.

type VerifyReport

type VerifyReport = statedb.AuditVerifyReport

VerifyReport re-exports statedb.AuditVerifyReport.

Jump to

Keyboard shortcuts

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