Help us shape the future of Go! Take the 2026 Go Developer Survey

audit

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package audit writes the console's JSONL audit trail: one record per event, never secrets and never row data. The file is opened O_APPEND, mode 0600 in a 0700 directory of the console account's state: the agent account cannot read or alter it, while the console account and root can. Each record carries prev_hash, the hash of the line before it, so that Verify detects a record edited, removed, inserted or reordered; records written before the chain stay readable (see Verify). On Linux, chattr +a makes the file append-only for the console account too.

Index

Constants

View Source
const (
	EventLogin     = "login"
	EventPolicy    = "policy"    // policy change, applied or refused (see Decision)
	EventRefused   = "refused"   // request refused by the policy
	EventApproved  = "approved"  // the human approved
	EventDenied    = "denied"    // the human denied
	EventTimeout   = "timeout"   // approval timed out
	EventAbandoned = "abandoned" // the client left while its approval was pending
	EventAuto      = "auto"      // auto-approved under auto mode (older logs: the console has no auto mode now)
	// EventPending is a request handed to the hub (Ticket set): decided
	// there, later. It is written before the requester is told pending:
	// a request whose pending record fails is refused, and its ticket
	// withdrawn.
	EventPending = "pending"
	// EventRun is the outcome of an approved or auto-approved request
	// (Decision ok or failed). It follows the approval record, which is
	// written before anything runs.
	EventRun     = "run"
	EventCatalog = "catalog" // catalog read
	// EventMode, EventResults and EventUnmask are the console's switches,
	// changed from its keyboard: Decision is the new state (shown or
	// hidden; allowed or off). EventMode (manual or auto) is in older
	// logs only: the console has no auto mode now.
	EventMode    = "mode"
	EventResults = "results"
	EventUnmask  = "unmask"
	EventLogout  = "logout"
	// EventReveal is a hub request to show a ticket's clear screen on a
	// local facade (Ctrl-O): Decision sealed or refused, with the ticket,
	// the approver and the facade. EventRenew is a request to ask again
	// for an expired ticket (Decision renewed or refused).
	EventReveal = "reveal"
	EventRenew  = "renew"
	// EventHubRequest is a hub request refused before its kind is known
	// (it did not verify).
	EventHubRequest = "hub-request"
)

Events of the console.

View Source
const Genesis = "genesis"

Genesis is the prev_hash of the first record of an empty log.

Variables

This section is empty.

Functions

func LineHash added in v0.3.0

func LineHash(line []byte) string

LineHash is the prev_hash of the record that follows line: "sha256:" and the hex SHA-256 of the line's bytes, without its newline.

Types

type Anchor added in v0.3.0

type Anchor struct {
	Line int
	Hash string
}

Anchor is a chained line of a log, kept apart from it (by the hub): the 1-based Line and its LineHash. Summary.Anchor makes one.

type Base

type Base struct {
	Event    string `json:"event"`
	Profile  string `json:"profile,omitempty"`
	Decision string `json:"decision,omitempty"`
	Error    string `json:"error,omitempty"`
	// Code is the refusal code of a refused record (package refusal), and
	// Span where the refusal points in the request, when known.
	Code     string        `json:"code,omitempty"`
	Span     *refusal.Span `json:"span,omitempty"`
	Warnings []string      `json:"warnings,omitempty"`
	// Intent is the agent's one-line reason for a request (untrusted
	// text), kept on every record of that request.
	Intent string `json:"intent,omitempty"`
	// Reason is the human's reason for a denial, escaped.
	Reason string `json:"reason,omitempty"`
	// Approval is how the human approved: "session" (typed in a login
	// session of the console account), "desktop+typed" (typed in a desktop
	// window, level 1) or "desktop+fingerprint" (level 2).
	Approval string `json:"approval,omitempty"`
	// The client behind a request, from the kernel's peer credentials:
	// set on the records of client requests only. PeerUID is nil when
	// unknown (uid 0 is root); PeerUser is the account name when it
	// resolves. The process name is the agent's choice and is not kept.
	PeerUID  *int   `json:"peer_uid,omitempty"`
	PeerPID  int    `json:"peer_pid,omitempty"`
	PeerUser string `json:"peer_user,omitempty"`
	// The hub's decision behind a record, in hub mode only: the ticket
	// ID, who answered it and on which facade (from the signed decision),
	// and the hub's signature of that decision, so that the record can be
	// checked against the hub's key later.
	Ticket   string           `json:"ticket,omitempty"`
	Approver *ticket.Approver `json:"approver,omitempty"`
	Facade   string           `json:"facade,omitempty"`
	HubSig   string           `json:"hub_sig,omitempty"`
}

Base is the product-neutral record: a product with no fields of its own writes it as is; another declares its own struct (with the same JSON names for these fields) and implements Record.

func (Base) AuditEvent

func (b Base) AuditEvent() string

AuditEvent implements Record.

type ChainError added in v0.3.0

type ChainError struct {
	Line   int
	Reason string
}

ChainError is a break of the chain, at a 1-based line of the log.

func (*ChainError) Error added in v0.3.0

func (e *ChainError) Error() string

type Log

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

Log appends records to <stateDir>/<product>/audit.log. The file is opened in append mode for each record, so it is never truncated and never held open. Logs of one file in one process share a lock, and every append holds a lock on the file itself, so that each record links to the line written just before it, whatever process writes it.

func Open

func Open(p kit.Product, stateDir string) (*Log, error)

Open prepares the audit log of p under stateDir (kit.StateBase() in production): the directory <stateDir>/<p.Name> is 0700 and the file 0600 (Unix), created if missing; existing content is kept.

func (*Log) Append

func (l *Log) Append(rec Record) error

Append writes rec as one JSON line: {"ts": the current time in RFC 3339 with milliseconds, then rec's fields in their encoding order, then "prev_hash": the LineHash of the file's last line (Genesis in an empty file)}. The chain comes last, so that a product's line keeps its format before the chain as a prefix; a record may not name prev_hash itself. A last line cut short is left as it is and the record starts a line of its own. An error means nothing was written, or the write failed: the caller must not treat the event as audited.

func (*Log) Path

func (l *Log) Path() string

Path is the audit log file.

type Record

type Record interface {
	AuditEvent() string
}

Record is one audit event of a product. Its JSON encoding must be an object whose fields hold no password and no row or message data: Error and every free text must already be sanitised by the caller. AuditEvent is the record's event name, never empty.

type Summary added in v0.3.0

type Summary struct {
	// Records is the number of lines, Legacy those written before the
	// chain (without prev_hash), at the start of the log.
	Records, Legacy int
	// Last is the LineHash of the last chained line, "" when none: an
	// anchor kept elsewhere (the hub's own log) detects a cut tail.
	Last string
}

Summary is what Verify read.

func Verify added in v0.3.0

func Verify(r io.Reader) (Summary, error)

Verify reads an audit log and checks its chain: every line is a JSON object; lines without prev_hash may only precede the first chained one (the format before the chain); from there, each prev_hash is the LineHash of the line before it, and Genesis only on the first line. A record edited, removed, inserted or reordered breaks the chain. Records cut from the end, or a chain stripped of every prev_hash, leave a log that verifies: VerifyAnchored checks it against an anchor kept elsewhere. It returns a *ChainError for a break.

func VerifyAnchored added in v0.3.0

func VerifyAnchored(r io.Reader, a Anchor) (Summary, error)

VerifyAnchored is Verify for a log anchored at a: the log reaches line a.Line, that line is chained and hashes to a.Hash. A log cut before the anchor, or whose chain was stripped (lines without prev_hash up to the anchor: a log that Verify alone reads as legacy) is a *ChainError.

func (Summary) Anchor added in v0.3.0

func (s Summary) Anchor() Anchor

Anchor is the anchor of the log as read: its last line, when chained.

Jump to

Keyboard shortcuts

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