Expand description
decern-ledger — the tamper-evident decision ledger (the audit column).
Every authority decision is appended as a hash-chained, Ed25519-signed
record: hash = SHA-256(entry_bytes ‖ prev_hash), signature over hash.
Any edit, reorder or in-place deletion breaks the chain; a wholesale
rewrite fails signature verification against the ledger key. What the
chain alone cannot detect is truncation of the tail — that is what
root() is for: export the head hash and anchor it externally (a
regulator, a notary, another system). Anchored root + intact chain =
complete, unmodified history.
The chain hash covers the EXACT entry bytes as stored on disk (captured
via serde_json’s RawValue at verify time), never a re-serialization — so
byte-stability is structural, not an assumption about JSON round-trips.
(Float round-tripping is NOT stable in serde_json without the
float_roundtrip feature; hashing re-serialized bytes was a confirmed
false-tamper bug that could brick an honest ledger.)
Re-exports§
pub use jcs::canonicalize;pub use jcs::digest;pub use sharded::ShardVerification;pub use sharded::ShardedLedger;pub use sharded::UNATTRIBUTED_SHARD;pub use sharded::verify_sharded_dir;
Modules§
- jcs
- RFC 8785 JSON Canonicalization Scheme (JCS) +
digest. - merkle
- RFC 9162 (Certificate Transparency 2.0) Merkle tree — the compact-proof layer over the linear hash chain. The chain already proves a record’s bytes and its position relative to its neighbours; a Merkle tree over the per-record hashes adds two things the chain cannot give compactly:
- sharded
ShardedLedger— the hosted-topology ledger: one hash chain per authority-domain (“shard”), safely extendable by any number of concurrentdecern-serverreplicas via adecern_store::LedgerHeadStore.
Structs§
- Asserted
By - The verified caller of the request that produced a record: the token’s subject, the client acting for it, and the issuer that vouched — enough for a reader to ask the right party why this request was made, and nothing a caller can write for itself.
- Bundle
Verdict - The per-check result of verifying an exported evidence bundle offline.
acceptedis the AND of every APPLICABLE check (anOptioncheck that isNonedid not apply and does not gate acceptance). Serializable so a CLI can emit it as--jsonverbatim. - Challenge
Record - A challenge and its answer, on the record.
- Checkpoint
- A signed, externally-anchorable commitment to the ledger’s state at a moment:
the head
rootover the firstcountentries, timestamped and signed by the ledger key. It leaks no entry content — only a hash, a count, and a signature — so it is safe to publish, hand to a notary, or submit to a SCITT transparency service. Because the log is append-only, the root over the firstcountentries is fixed forever; an external party holding a checkpoint can re-derive that root from the file and, if it disagrees, prove the operator rewrote history — the operator-INDEPENDENT verification a log inside the operator’s own stack lacks. - Consistency
Proof - A compact RFC 9162 consistency proof (hex-encoded) that the tree of the first
first_sizeleaves is an exact prefix of the tree ofsecond_sizeleaves. - Decision
Subject - The party a decision is about, as a pseudonymous reference.
- Entry
- One record in the ledger: a decision — “what happened, with everything needed to replay it”. Several fields below are reserved and inert (see each field’s note): no shipped path sets them, they are retained only for struct/type stability, and a plain decision leaves them at their defaults, which serialize to no bytes — so every existing writer and stored line is unchanged.
- Inclusion
Proof - A compact RFC 9162 inclusion proof (hex-encoded): the record at
leaf_indexin a tree oftree_sizeleaves is committed by aTreeHead’s root.leaf_datais the record’s chain hash (the Merkle leaf data — a verifier hashes it with the0x00leaf prefix);audit_pathis the sibling hashes bottom-up. - Ledger
- Append-only writer. Opening an existing ledger verifies the whole chain (and, with this key, every signature) before accepting new entries — fail-closed: a corrupt audit trail refuses further writes.
- Mission
Ref - A Mission reference recorded on a decision Entry.
- Party
- A party referenced by a record — the accountable owner named by
sponsor, or thedecision_subject. The acting subject issubject_idon the entry. - Record
- Rollover
Policy - When a segmented ledger rolls its active segment over to a new file. Both
may be set (roll over on whichever fires first); both
Noneis legal but pointless (a segmented ledger with exactly one ever-growing segment). - Tree
Head - A signed, externally-anchorable commitment to the ledger’s MERKLE state: the RFC 9162
tree root over the first
tree_sizerecord hashes, timestamped and signed by the ledger key. Parallel toCheckpoint(the linear-chain head): aTreeHeadenables COMPACT third-party proofs — an inclusion proof shows one record is in the log without shipping the whole tail, and a consistency proof between an anchored earlierTreeHeadand a later one proves nothing below the earlier size was rewritten or dropped (closing equivocation). Leaks no entry content — only a root, a size, and a signature. - Verify
Report
Enums§
- Edge
Type - How an authority-graph edge came to be — the attenuate-vs-mint distinction.
Typing every issuance edge lets the record tell offline narrowing apart from a
trusted-issuer crossing; the two carry different safety properties (only the
former is safe to delegate offline). Only
Attenuateis used;Mintis reserved and inert, retained so the enum stays stable. - Ledger
Error - Sponsor
Source - How
Entry::sponsorwas determined (seeEntry::sponsor).
Constants§
- DIGEST_
AUTHORITY - The authority a decision was taken against — policy, schema and entity graph.
- DIGEST_
PARAMETERS - The exact arguments a decision authorized — what it was asked, not what it knew. Binding them means a later reading can tell that the thing authorized is the thing that was requested, rather than something substituted after the check.
- GENESIS
Functions§
- inclusion_
proofs_ over - Inclusion proofs over already-derived leaves — the proving half of
Ledger::inclusion_proofs, for a caller who took the leaves out from under the lock. Returns proofs in the order the sequences were given; a sequence past the end fails the whole call rather than being skipped. - leaves_
from_ lines - Verify a checkpoint’s own signature against a pinned key — does the ledger key
that signs entries also vouch for this commitment? Does not read the ledger.
The Merkle leaf data of each record, in order: the 32 raw bytes its
hashhex encodes. One definition, shared by the signing side and the read-only verifying side — a leaf that meant two different things in two places would produce proofs that verify nowhere. The Merkle leaf data of each stored line, in order — the record-form derivation, for a caller holding raw lines fromLedger::raw_records. Only thehashfield is deserialized, and a line without a valid one fails closed exactly as the record form does. - ledger_
extends_ checkpoint - The operator-independent tamper check: does the ledger at
pathstill extend a previously issuedcp? Re-derives the head over the firstcp.countrecords from the stored bytes and confirms it equalscp.root. If the operator edited, reordered, or truncated any entry at or beforecount, the re-derived root diverges and this returnsOk(false)— caught by anyone holding the old checkpoint, without trusting the operator. A well-behaved append-only log always extends its past checkpoints. - load_
anchor - Load the persisted anchor, or
Noneif no anchor file exists yet. A present but unparseable anchor is a hard error (fail-closed — a corrupt anchor is never silently treated as “no committed height”). - merkle_
leaves_ at - Merkle leaves of the ledger at
path, read-only — no signing key required. - read_
verified - Verify the whole chain (fail-closed on any tamper) AND return a window of the parsed
records — the OFFLINE auditor read: an auditor holds the ledger file and, out of band,
the public key, but not the private signing key
Ledger::opendemands. Withpubkeyeach record’s signature is checked too; without it, only the hash chain (still fail-closed). The whole log is scanned to verify integrity; only records in[offset, offset+limit)are materialized (memory stays bounded to the window). The records are the exact stored JSON (asread_recordsreturns them), NOT verbatim bytes. - save_
anchor - Persist a checkpoint as the ledger’s anchor file, atomically (temp + fsync +
rename + parent-dir fsync) so a crash cannot leave a half-written or non-durable
anchor — a lost anchor write would silently lower the height truncation is checked
against. See
Ledger::seal_anchor. - verify
- Verify a ledger file: the hash chain always; every entry signature when a key is
supplied. Single-key convenience over
verify_with_keys— for a key-ROTATED log (entries under more than one key) use that with the full keyring. - verify_
checkpoint_ sig - verify_
evidence_ bundle - Verify an exported
decern-evidence-bundleOFFLINE against a PINNED keyring — the standalone third-party check with no call back to the server.bundle_jsonis the RAW bundle file text (NOT a re-serializedValue): each record’sentryis captured as verbatim bytes, because the chain commits to those exact bytes.keysis the pinned current + retired public keys (obtained OUT OF BAND — never from the bundle).against, if given, is an independently-anchored EARLIER tree head; a consistency check then proves the bundle did not rewrite or drop anything below that earlier size (equivocation/truncation). - verify_
tree_ head_ sig - Verify a tree head’s own signature against a pinned key — the Merkle counterpart of
verify_checkpoint_sig. The domain-separateddecern-ledger-tree-headtag means this never cross-verifies a checkpoint signature. Does not read the ledger. - verify_
with_ keys - Verify a ledger file against a KEYRING — the rotation-aware form. Each record is
checked against the key its
kidnames; a legacy record with nokid(written before rotation support) is accepted against any key in the ring. A record whosekidnames a key NOT in the ring is tamper (fail-closed: an unknown signer is never trusted). An empty ring means “signatures not checked” (chain only), same asverifywithNone.
Type Aliases§
- Evidence
Snapshot - A single-snapshot evidence bundle: record count, raw bytes, and signed commitments
(checkpoint and merkle tree head) all derived from the same log state at one moment.
This is the return type of
Ledger::snapshot_for_bundleandShardedLedger::evidence_snapshot.