Skip to content
internal/trace
On this page

API reference for the trace package — reads the strictcli process trace store to resolve which tool invoked a deletion.

#internal/trace

#internal/trace

Package trace reads the strictcli process trace store, so a deletion can record who ran it.

The store is a shared, append-only JSONL record of process ancestry: at the seam where one command-line tool spawns another, the spawning invocation writes one line describing itself and hands that line's identifier to the child in STRICTCLI_TRACE_PARENT. saferm is a consumer, never a writer. It parses the variable and reads the store itself, per the normative specification (strictcli's docs/process-trace-store.md), because the framework deliberately exposes no accessor for the ancestry stack -- nothing in the framework may branch on data no framework code reads.

Everything here is observational. A capture cannot fail a deletion: an absent variable, a polluted one, a pruned store, a torn line and a parent that resolves to nothing are all legal states, each recorded as an anomaly and carried on from. Consumers noticing dangling parents is the store's primary failure-detection channel, which is why the anomalies are written into the record rather than discarded.

A capture resolves the FULL ancestry chain at capture time and keeps the flattened entries, not only their identifiers, so the record stays self-contained: age-based pruning of the store can never orphan it. The identifiers are kept alongside for correlation with whatever store data still exists.

#ParentEnv

Go go
const ParentEnv = "STRICTCLI_TRACE_PARENT"

ParentEnv is the one variable ancestry travels through. It carries exactly one thing: the identifier of the entry describing the process that spawned this one.

#AnomalyMalformedParentValue

Go go
const AnomalyMalformedParentValue = "malformed-trace-parent"

AnomalyMalformedParentValue: the environment variable was set to something that is not a canonical identifier. Recorded verbatim.

#AnomalyDanglingParent

Go go
const AnomalyDanglingParent = "dangling-parent"

AnomalyDanglingParent: an identifier resolved to no entry -- the store was pruned or missing, the writer was another tool, or someone set the variable by hand. Legal by design, and the store's primary failure-detection channel.

#AnomalyMalformedEntry

Go go
const AnomalyMalformedEntry = "malformed-entry"

AnomalyMalformedEntry: a line in a partition could not be read as an entry -- torn by a non-atomic write, missing one of the thirteen keys, or carrying an unparseable identifier.

#AnomalyStoreUnreadable

Go go
const AnomalyStoreUnreadable = "store-unreadable"

AnomalyStoreUnreadable: a partition or the store directory could not be read at all. A store that does not exist is NOT this: that is an ordinary dangling parent.

#AnomalyChainCycle

Go go
const AnomalyChainCycle = "chain-cycle"

AnomalyChainCycle: walking parent_id revisited an identifier. No store a conforming writer produces can contain one, since an entry's parent is always older than itself.

#AnomalyOversizedField

Go go
const AnomalyOversizedField = "oversized-entry-field"

AnomalyOversizedField: a string an entry contributes to the embedded chain was longer than a chain entry may carry, and was truncated. The line was conforming -- nothing in the entry rules bounds a value's length -- so this is the consumer stating what it kept, not a complaint about the writer.

#AnomalyAnomaliesDropped

Go go
const AnomalyAnomaliesDropped = "anomalies-dropped"

AnomalyAnomaliesDropped: the capture saw more anomalies than one record may carry. Synthetic, always last, and present only when something was dropped: it names how many, so a truncated anomaly list can never read as a complete one.

#Entry

Go go
type Entry struct

Entry is one line of the store: an invocation that spawned a child. Every key is always present in a conforming line, so an absent one makes the line malformed rather than defaulted.

#Anomaly

Go go
type Anomaly struct

Anomaly is something the capture saw and could not treat as well-formed.

#Capture

Go go
type Capture struct

Capture is what one deletion records about its ancestry.

Chain holds the flattened ancestry, nearest caller first, so the record stays readable after the store is pruned. ChainIDs is the same walk as bare identifiers, kept for correlation with whatever store data still exists.

#Collect

Go go
func Collect() *Capture

Collect resolves the ancestry of the running process from the store.

It returns nil when STRICTCLI_TRACE_PARENT is unset -- nothing claimed this invocation, which is not an anomaly and is the state every deletion is in until callers upgrade to a framework that writes the store.

#StoreDir

Go go
func StoreDir(home string) string

StoreDir is the store's literal path under home.

It is deliberately NOT derived from XDG_DATA_HOME or any other variable, despite matching the XDG default: a writer that honoured XDG_DATA_HOME and one that did not would write to two stores on the same machine, and a chain crossing them would dangle at both ends while both writers behaved correctly.

#Capture.Origin

Go go
func (c *Capture) Origin() (name, version *string)

Origin is the immediate caller's declared name and version -- the two values a deletion records as its origin. Both are nil when nothing resolved, which is what "no tool claimed this" means.

Search