Ledger
The ledger is Sigil’s source of truth. Every evaluation, decision, trust transition, human override, and incident report is recorded as an event in an append-only log. The log is git-backed, hash-chained, and projected into a SQLite view for queries.
Event types
Section titled “Event types”eval.complete— an evaluation finished. Carries the reproducibility tuple, per-scenario results, baseline comparison, score, token usage, ascenario_loadtally, theevaluator_digestandjudge_config_hash, and anyendpoint_override/deploy_recipe_hash.eval.decision— asigil decideinvocation resolved to ALLOW, REVIEW, or BLOCK, with its rationale codes.trust.transition/trust.decay— the service’s earned trust level changed. Since 0.36.0sigil decidewrites these; see Trust Model.override.record— a human overrode a decision. Records the reason code, ticket, and note.incident.report— a production incident attributed to a PR. Feeds trust scoring and downgrades.policy.mode_set— the operator changed a service’s policy mode.
Other kinds record holdout decryption, judge calibration, scenario quarantine, and layout migrations.
Format
Section titled “Format”Events are stored as TSJSON, one event per line, in shard files. Each event records its writer (agent), that agent’s sequence number (seq), the content hash of the event its writer appended before it (parent_hash), and its own content hash. A line is re-hashed when it is read, so an edited line fails to load. Shards merge with git’s union strategy, so concurrent writers fork the chain into a DAG. A fork is normal.
Since 0.36.0 an append takes a ledger-wide lock (.append.lock in the ledger root) and re-reads the ledger before it chooses the event’s seq and parent, so two sigil processes on one ledger no longer reuse a seq. sigil ledger sync holds the same lock through its pull. A sync that was killed mid-rebase is detected and aborted on the next open, append, or sync, and a torn final line is terminated so that it stays alone on its line.
Storage
Section titled “Storage”- Authoritative — the git repository. Git distribution gives replication and audit history.
- Projection — a local SQLite view for fast queries. It is rebuilt from the log on demand (
sigil ledger rebuild) and is never authoritative. Since 0.36.0 incremental replay projects every event it has not seen, including events a sync pulls late, so it always matches a full rebuild.
Verifying the chain
Section titled “Verifying the chain”sigil ledger verifysigil ledger verify checks that:
- every line parses and its content hash matches,
- every parent hash names an event in the ledger, and
- each agent’s
seqvalues run 0..n with no gap.
It exits 1 when the chain is broken. sigil decide runs the same check before and after its pre-ALLOW sync, and REVIEWs with ledger_chain_broken. Duplicate seqs are reported but accepted, because clones that share an agent name reuse a seq until they sync.
This detects accidental corruption, and the removal or editing of any event that later events depend on. It is not tamper evidence. The content hash is unkeyed, so someone who can write the ledger can remove the newest events, reorder events, or rewrite an event and rehash everything after it, and the check still passes. Ledger events are not signed. To bind a decision to a key, configure signed decision attestations.
Querying
Section titled “Querying”sigil trust show --service apisigil trust history --service api --days 7sigil report <eval-id> --service apisigil diff <eval-a> <eval-b>sigil replay <eval-id>sigil replay verifies and consumes the evidence pinned by a past eval. sigil replay --live re-executes the scenarios; since 0.36.0 it refuses a scenario set that differs from the recorded scenario_set_hash, and uses the configured judge and reset hooks. Judge verdicts come from a fresh LLM call, so they are not byte-reproducible.
Freshness
Section titled “Freshness”See Invariants — Freshness Gate. Before it emits an ALLOW, sigil decide syncs the ledger with its origin; the sync must finish within [ledger] max_staleness_for_allow, and the pulled events are checked again before the ALLOW goes out.