Documentation
¶
Overview ¶
Package audit is tailkeeper's append-only, hash-chained JSONL log (spec §14).
Each line carries seq (1, 2, ...), prev (the previous line's hash, "" for the first) and hash ("sha256:" + hex of the canonical JSON of the line without its hash field). A plain SHA-256 chain detects damage and naive edits; it does not stop someone who can rewrite the file and recompute the chain, and it cannot tell when lines were removed from the end. Exports anchor both (spec §14).
Rotate starts a new log and keeps the old one beside it as an archive; the new log's first entry records where the archive ends, and Verify checks it.
Index ¶
Constants ¶
const FileName = "audit.jsonl"
FileName is the audit log's name inside the logs directory.
const MaxLine = 32 << 10
MaxLine is the largest line Append writes. Append reads the previous line from the last 64 KiB of the file, so every line must fit well inside that.
const Version = 2
Version is the line format version.
Variables ¶
This section is empty.
Functions ¶
func RotateSupported ¶
func RotateSupported() bool
RotateSupported reports whether Rotate works here: it needs the file lock, which Windows does not have yet.
Types ¶
type Log ¶
type Log struct {
// contains filtered or unexported fields
}
Log appends to one audit file. It is safe for concurrent use by several processes: appends take an exclusive file lock. Windows has no file lock yet: there the appends of one process exclude each other, and two processes can still fork the chain.
func (*Log) Append ¶
Append writes one event. fields must be JSON-marshalable and must not use the reserved names v, seq, ts, prev, event or hash.
func (*Log) Rotate ¶
Rotate starts a new log and keeps the old one beside it as an archive named audit-<UTC time>.jsonl, which it returns. The new log's first entry, event "rotated", names the archive and its last seq and hash: Verify checks that a present archive still ends there, and VerifyHistory also checks its chain.
Rotate refuses a log that fails verification, so a rotation never hides damage, and a symbolic link, which the rename would replace with a file. The log path never goes missing: the archive is a hard link made first, and the new log replaces the path in one rename. Archives are kept until the owner deletes them.
func (*Log) SetLockWait ¶
SetLockWait sets how long Append waits for a lock someone else holds; 0 restores the default of 2 seconds.
type Segment ¶
type Segment struct {
Path string
Lines int // valid lines
Missing bool // named by the newer file's rotation but not present, so not checked
}
Segment is one file of a log's history.
func VerifyHistory ¶
VerifyHistory verifies the log and then, through the rotation each file starts with, every archive before it that is still present, newest first. A missing archive ends the history, and its Segment says so.