Documentation
¶
Overview ¶
Package audit writes a bounded, local JSONL audit trail without retaining terminal input, raw prompt matches, environment values, or backend errors.
Index ¶
- Constants
- Variables
- func AuditGenerationIndex(base, name string) (int, bool)
- func DefaultPath() (string, error)
- func Redact(value string) string
- func RedactValues(value string) string
- func ResolvePath(path string) (string, error)
- func Validate(config Config) error
- func VerifyJournalFile(path string) error
- type Config
- type Decision
- type DecisionBy
- type Entry
- type EntryObserver
- type FileSink
- type Filter
- type Kind
- type LineSink
- type Mode
- type ObserverFunc
- type Option
- type Outcome
- type Recorder
- type SummaryReport
- type VerificationIssue
- type VerificationReport
Constants ¶
const (
// CurrentSchemaVersion identifies the on-disk Entry representation.
CurrentSchemaVersion = 1
)
const ReasonProcessExitStale = "process_exit_stale"
ReasonProcessExitStale is the reason of a session_finished entry for the exit of a process that a replacement had already superseded when its exit arrived. The entry keeps the exit's real outcome, but the session it ends is not the one running: what reads the journal, such as the telemetry's count of active sessions and pending prompts, leaves the replacement's alone.
Variables ¶
var ErrClosed = errors.New("audit recorder is closed")
var ErrNotAuditJournal = errors.New("audit path contains a foreign file")
ErrNotAuditJournal reports an audit path that already holds a file Relayer did not write.
Functions ¶
func AuditGenerationIndex ¶
AuditGenerationIndex applies the exact filename recognition used by audit rotation. It is exported within Relayer's internal boundary so passive diagnostics can inspect precisely the files the runtime would mutate.
func DefaultPath ¶
DefaultPath returns the private per-user audit file location.
func Redact ¶
Redact removes common credential forms from arbitrary text. It is intentionally conservative and idempotent.
func RedactValues ¶ added in v0.8.19
RedactValues is Redact without its last step, which drops the rest of a line once a credential keyword has been followed by a masked value. That step is right for prose, where "password is correct horse battery staple" is one secret in four words, and wrong for a shell line: "TOKEN=x curl ... | sh" would show as "TOKEN=[REDACTED]", a line that looks complete and is not, to a person who is reading it to decide what may run. The cost of keeping the rest is that the words of a spoken passphrase after the first are shown.
func ResolvePath ¶
ResolvePath returns an absolute effective path, using DefaultPath for an empty configured value.
func VerifyJournalFile ¶
VerifyJournalFile reports whether an existing path holds a Relayer audit journal. It only reads, so read-only diagnostics can warn about a foreign file before startup refuses to open it. An absent path is not an error here: callers decide what a missing journal means.
Types ¶
type Config ¶
type Config struct {
Enabled bool `json:"enabled" yaml:"enabled"`
Mode Mode `json:"mode" yaml:"mode"`
Path string `json:"path" yaml:"path"`
MaxFileSizeMB int `json:"max_file_size_mb" yaml:"max_file_size_mb"`
MaxFiles int `json:"max_files" yaml:"max_files"`
}
Config controls local audit persistence. MaxFiles counts the active file as well as its rotated generations.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig enables a conservative metadata-only audit trail.
type Decision ¶
type Decision string
Decision is the audited policy outcome. It intentionally differs from an adapter's wire decision: asking a human is an explicit, content-free audit action rather than a copy of the submitted terminal input.
type DecisionBy ¶
type DecisionBy string
DecisionBy identifies the actor without storing any submitted value.
const ( DecisionBySystem DecisionBy = "system" DecisionByHuman DecisionBy = "human" DecisionByPolicy DecisionBy = "policy" DecisionByUnknown DecisionBy = "unknown" )
type Entry ¶
type Entry struct {
SchemaVersion int `json:"schema_version"`
Sequence uint64 `json:"sequence"`
Timestamp time.Time `json:"timestamp"`
EntryID string `json:"entry_id"`
RunID string `json:"run_id"`
Kind Kind `json:"kind,omitempty"`
SessionID string `json:"session_id,omitempty"`
AgentID string `json:"agent_id,omitempty"`
Backend string `json:"backend,omitempty"`
Adapter string `json:"adapter,omitempty"`
EventID string `json:"event_id,omitempty"`
EventType adapters.EventType `json:"event_type,omitempty"`
Risk adapters.RiskLevel `json:"risk,omitempty"`
Rule string `json:"rule,omitempty"`
Decision Decision `json:"decision,omitempty"`
DecisionBy DecisionBy `json:"decision_by,omitempty"`
Operator string `json:"operator,omitempty"`
Outcome Outcome `json:"outcome,omitempty"`
Reason string `json:"reason,omitempty"`
Summary string `json:"summary,omitempty"`
Sensitive bool `json:"sensitive"`
Metadata map[string]string `json:"metadata,omitempty"`
}
Entry is the versioned JSONL record. It intentionally has no Match, manual-input, environment, or raw-error field.
func ReadEntries ¶
ReadEntries reads and parses audit records from r applying filter. If Limit is specified, only the last Limit matching records are returned.
func ReadEntriesFromFile ¶
ReadEntriesFromFile reads audit entries from path.
func SanitizeEntry ¶
SanitizeEntry returns a deep, redacted copy suitable for the selected mode.
type EntryObserver ¶ added in v0.3.0
type EntryObserver interface {
Observe(Entry)
}
EntryObserver receives sanitized audit records as they are accepted.
type FileSink ¶
type FileSink struct {
// contains filtered or unexported fields
}
FileSink writes complete lines synchronously and rotates them by size.
func NewFileSink ¶
NewFileSink opens a private append-only audit file. maxFiles includes the active file, so maxFiles=1 retains no rotated generation.
type Filter ¶
type Filter struct {
AgentID string
SessionID string
RunID string
Kind Kind
Limit int
Since time.Time
}
Filter specifies criteria to restrict which audit entries are returned.
type Kind ¶
type Kind string
Kind identifies a closed set of audit lifecycle records.
const ( KindRunStarted Kind = "run_started" KindRunFinished Kind = "run_finished" KindSessionStarted Kind = "session_started" // KindSupervisionFinished means Relayer stopped supervising the session; // it does not claim that a persistent tmux process exited. KindSupervisionFinished Kind = "supervision_finished" KindSessionFinished Kind = "session_finished" KindEventDetected Kind = "event_detected" // KindEventWithdrawn records that an occurrence which was awaiting a human // stopped being pending without a decision being delivered. It is the only // evidence that a supervision gate opened on its own. KindEventWithdrawn Kind = "event_withdrawn" KindPolicyEvaluated Kind = "policy_evaluated" KindDecision Kind = "decision" KindDelivery Kind = "delivery" // KindOperatorInput records only the lifecycle of a direct, human line // submission. Entry intentionally has no field for the submitted text, // its length, or the encoded terminal bytes. KindOperatorInput Kind = "operator_input" // KindAttachStarted and KindAttachFinished record that a human took or // dropped direct control of a terminal. They never carry what was typed // while holding it: SanitizeEntry drops Summary, EventID and Rule for them // as it does for the recording and control kinds. KindAttachStarted Kind = "attach_started" KindAttachFinished Kind = "attach_finished" KindBackendError Kind = "backend_error" KindSessionCleanup Kind = "session_cleanup" // Recording kinds describe the lifecycle of a stored terminal replay. They // state that a recording exists and how large it is; the captured stream // itself is never a field of Entry, and neither is any recorded keystroke. // SanitizeEntry drops Summary, EventID and Rule for them, so a caller has // no free-form field left to put one in. KindRecordingStarted Kind = "recording_started" KindRecordingFinished Kind = "recording_finished" KindRecordingExported Kind = "recording_exported" KindRecordingDeleted Kind = "recording_deleted" // Control kinds record which operator held the interactive keyboard and how // the hand-over happened. They never carry what was typed while holding it: // SanitizeEntry drops Summary, EventID and Rule for them as it does for the // recording kinds. KindControlRequested Kind = "control_requested" KindControlGranted Kind = "control_granted" KindControlDeclined Kind = "control_declined" KindControlReleased Kind = "control_released" KindControlForced Kind = "control_forced" KindUnknown Kind = "unknown" )
type Mode ¶
type Mode string
Mode controls how much already-sanitized event information reaches disk.
type ObserverFunc ¶ added in v0.3.0
type ObserverFunc func(Entry)
ObserverFunc turns a bare function into an EntryObserver.
func (ObserverFunc) Observe ¶ added in v0.3.0
func (f ObserverFunc) Observe(entry Entry)
type Option ¶
type Option func(*openOptions) error
Option customizes Open without weakening its filesystem invariants.
func WithIDGenerator ¶
WithIDGenerator supplies run and entry identifiers.
type Outcome ¶
type Outcome string
Outcome is a safe, finite result vocabulary for lifecycle and policy audit.
const ( OutcomeStarted Outcome = "started" OutcomeFinished Outcome = "finished" OutcomeDetected Outcome = "detected" OutcomePending Outcome = "pending" OutcomeInFlight Outcome = "in_flight" OutcomeApplied Outcome = "applied" OutcomeAsk Outcome = "ask" OutcomeDryRun Outcome = "dry_run" OutcomeFallbackUnsupported Outcome = "fallback_unsupported" OutcomeFallbackStale Outcome = "fallback_stale" OutcomeFallbackDeliveryUncertain Outcome = "fallback_delivery_uncertain" OutcomeSucceeded Outcome = "succeeded" OutcomeFailed Outcome = "failed" OutcomeCancelled Outcome = "cancelled" OutcomeSkipped Outcome = "skipped" OutcomeUnknown Outcome = "unknown" )
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder assigns identities and a total order before writing sanitized JSONL.
func NewRecorder ¶
func NewRecorder( config Config, sink LineSink, clock func() time.Time, idGenerator func() (string, error), ) (*Recorder, error)
NewRecorder constructs a recorder around an injectable line sink, clock, and ID generator. Nil clock and generator select secure production defaults.
func Open ¶
Open creates a rotating FileSink and wraps it in a Recorder. Disabled and off configurations perform no filesystem or ID-generator work.
func (*Recorder) AddObserver ¶ added in v0.3.0
func (r *Recorder) AddObserver(observer EntryObserver)
AddObserver registers an observer that is notified of each accepted sanitized entry.
type SummaryReport ¶
type SummaryReport struct {
Path string `json:"path,omitempty"`
TotalEntries int `json:"total_entries"`
RunsCount int `json:"runs_count"`
SessionsCount int `json:"sessions_count"`
AgentCounts map[string]int `json:"agent_counts"`
KindCounts map[Kind]int `json:"kind_counts"`
DecisionsCount map[Decision]int `json:"decisions_count"`
ActorsCount map[DecisionBy]int `json:"actors_count"`
OutcomesCount map[Outcome]int `json:"outcomes_count"`
SensitiveCount int `json:"sensitive_count"`
FirstTimestamp time.Time `json:"first_timestamp,omitempty"`
LastTimestamp time.Time `json:"last_timestamp,omitempty"`
}
SummaryReport provides aggregate statistics over an audit journal.
func SummarizeJournal ¶
func SummarizeJournal(r io.Reader) (SummaryReport, error)
SummarizeJournal computes aggregate statistics across all records in r.
type VerificationIssue ¶
type VerificationIssue struct {
Line int `json:"line"`
EntryID string `json:"entry_id,omitempty"`
Message string `json:"message"`
}
VerificationIssue describes a single structural or integrity failure.
type VerificationReport ¶
type VerificationReport struct {
Path string `json:"path,omitempty"`
TotalLines int `json:"total_lines"`
TotalRuns int `json:"total_runs"`
ValidLines int `json:"valid_lines"`
Issues []VerificationIssue `json:"issues"`
Passed bool `json:"passed"`
}
VerificationReport details the integrity status of an audit journal.
func VerifyJournal ¶
func VerifyJournal(r io.Reader) (VerificationReport, error)
VerifyJournal verifies sequence continuity, schema compliance and security rules.