Three layers of protection
1
HMAC signature
The appliance signs each record’s canonical JSON (RFC 8785) with its HMAC
key. Every field the record sets is signed, except a short list of
fields that are unsigned by design.
2
Hash chain
Each record carries a
sequence_number and the prev_hash of the record
before it. Removing, inserting or reordering a record breaks the chain.3
Ed25519 countersignature
A second, asymmetric signature. Anyone with the appliance’s public key
can check it offline, without the HMAC key. Countersigning runs
asynchronously, shortly after ingest.
SENGOL_EVIDENCE_ANCHOR=1).
What a record contains
Export one agent’s records, then look at the first one:prev_hash. controlbook_id is
None because the demo records were written without a ControlBook.
Signed
- The verdict of every evaluator:
passed(true or false) and, on failure, a namedfailure_mode - Agent id and version, tenant, policies, and the ControlBook id and version when the record was written under one
- A UTC timestamp, fixed at write time
reason_digest: a keyed digest of each evaluator’s free-text reasonevaluator_versionandjudge_modelon each evaluator’s score, when the suite stamped themsample_basisandenvironment, on records written by online scoring of live calls
Not signed, by design
- The free-text
reason. It is stored separately so that a data-subject erasure can remove it. The record still verifies afterwards, and the reason reads back asERASED. hmac_signatureandsigning_backend, which describe the signature itself.
sengol-verify lists the unsigned fields it found in its field_coverage check.
Record families
What is not signed evidence
These write logs or labels, not signed records:- human review decisions
- user and role administration, including break-glass sign-ins
- ControlBook activation
Verifying
- Offline, by anyone:
sengol-verifychecks the canonical payload, the chain and the countersignatures. - Online, with appliance access:
sengol audit verify --chain --agent-id <id>also checks every HMAC.