audit

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package audit implements §12.7: the JSONL audit log of every write PayCLI performs.

Two records are written per operation — one before the call and one after — so an interrupted destructive operation still leaves a trace. Reads are never logged. No credential, JWT, Authorization header or document body ever reaches this file: every URL goes through redact.URL, every free-text field through redact.Text, and `where` through redact.JSON.

Index

Constants

View Source
const (
	// FileName is the log's name inside the config directory.
	FileName = "audit.log"
	// FilePerm is §4.1's mode for audit.log.
	FilePerm fs.FileMode = 0o600
	// DefaultMaxBytes is the §12.7 rotation threshold.
	DefaultMaxBytes int64 = 10 << 20
	// DefaultGenerations is how many rotated files are kept (audit.log.1 … .3).
	DefaultGenerations = 3
	// MaxIDs caps Event.IDs; the overflow is reported by IDsTruncated.
	MaxIDs = 200
	// WarnCode is the warning emitted when the POST-write record fails.
	WarnCode = "audit_write_failed"
)
View Source
const DefaultTail = 20

DefaultTail is `pay audit tail`'s default -n.

Variables

This section is empty.

Functions

This section is empty.

Types

type Event

type Event struct {
	Time         time.Time       `json:"time"`
	Phase        Phase           `json:"phase"`
	Command      string          `json:"command"`
	Profile      string          `json:"profile"`
	BaseURL      string          `json:"base_url"`
	Collection   string          `json:"collection,omitempty"`
	Global       string          `json:"global,omitempty"`
	Action       string          `json:"action"`
	Method       string          `json:"method"`
	Path         string          `json:"path"`
	Where        json.RawMessage `json:"where,omitempty"`
	IDs          []string        `json:"ids,omitempty"`
	IDsTruncated bool            `json:"ids_truncated,omitempty"`
	Affected     int             `json:"affected"`
	Failed       int             `json:"failed,omitempty"`
	DryRun       bool            `json:"dry_run,omitempty"`
	Status       int             `json:"http_status"`
	OK           bool            `json:"ok"`
	Err          string          `json:"error,omitempty"`
	DurationMS   int64           `json:"duration_ms"`
	RequestID    string          `json:"request_id"`
	// Backups are the §12.8 backup files written before this write, capped
	// like IDs (BackupsTruncated reports the overflow). They are local paths,
	// never document bodies.
	Backups          []string `json:"backups,omitempty"`
	BackupsTruncated bool     `json:"backups_truncated,omitempty"`
	// Step names one request of a write made of several (the orphan-row
	// remedy's phases: "reset-nested 1/2", "reset-nested final"). Empty for
	// an ordinary one-request write.
	Step string `json:"step,omitempty"`
	// RestoredFrom is the backup file a `pay backups restore` wrote back
	// (§12.8.8), so the log answers "which backup was put back". Backups
	// above are the files taken of the state it overwrote.
	RestoredFrom string `json:"restored_from,omitempty"`
}

Event is one audit record (§12.7).

Phase and IDsTruncated are not in §12.7's struct listing: Phase is required because a pre-record and a failed post-record are otherwise byte-identical, and §12.7's own comment on IDs ("capped at 200 + IDsTruncated") names the second one.

type Logger

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

Logger appends audit records.

func New

func New(opts Options) *Logger

New builds a Logger. It never returns an error: an unusable path surfaces at write time, where the §12.7 pre/post policy can be applied to it.

func (*Logger) Enabled

func (l *Logger) Enabled() bool

Enabled reports whether records are being written.

func (*Logger) Generations

func (l *Logger) Generations() []string

Generations lists the rotated files that currently exist, newest first, for `pay doctor`.

func (*Logger) Path

func (l *Logger) Path() string

Path is the log's location, for `pay doctor` and `pay audit tail`.

func (*Logger) Post

func (l *Logger) Post(e Event) *output.Warning

Post writes the after-the-call record. A failure here is a WARNING only (§12.7): the mutation already happened, and failing the command would report a false negative on a committed write. The returned warning is nil on success.

func (*Logger) Pre

func (l *Logger) Pre(e Event) error

Pre writes the before-the-call record. A failure here ABORTS the operation (§12.7): the user must opt out of auditing deliberately, never by accident.

func (*Logger) Tail

func (l *Logger) Tail(opts TailOptions) ([]Event, error)

Tail returns the most recent matching events in chronological order. Unparseable lines are skipped rather than failing the read: a truncated final line after a crash must not make the whole log unreadable.

func (*Logger) Writable

func (l *Logger) Writable() error

Writable reports whether the audit log can be appended to, for `pay doctor`. It creates the directory and an empty log if necessary, which is exactly what the first real write would do.

type Options

type Options struct {
	// Path is the audit log's absolute path. Empty disables the logger, which
	// keeps a mis-resolved config directory from failing every write.
	Path string
	// Disabled is --no-audit / PAY_NO_AUDIT=1.
	Disabled bool
	// MaxBytes overrides DefaultMaxBytes.
	MaxBytes int64
	// Generations overrides DefaultGenerations.
	Generations int
	// Now is the injected clock (§3.1: only app.go may call time.Now).
	Now func() time.Time
	// Secrets are resolved credential literals to scrub from every field, for
	// the case where one reached a string through a path §5.3's key-name rules
	// cannot see.
	Secrets []string
}

Options configures a Logger.

type Phase

type Phase string

Phase distinguishes the record written before the call from the one written after it.

const (
	// PhasePre is written before the request leaves PayCLI.
	PhasePre Phase = "pre"
	// PhasePost is written after the response (or failure) is known.
	PhasePost Phase = "post"
)

type TailOptions

type TailOptions struct {
	// N is the maximum number of events returned, most recent last. 0 means
	// DefaultTail.
	N int
	// Since drops events older than this instant. Zero means no bound.
	Since time.Time
	// Action, Command, Profile and Collection are exact-match filters; empty
	// means "any".
	Action     string
	Command    string
	Profile    string
	Collection string
	// Phase filters pre/post records; empty means both.
	Phase Phase
	// IncludeRotated also reads audit.log.1 … .N.
	IncludeRotated bool
}

TailOptions filters `pay audit tail`.

Jump to

Keyboard shortcuts

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