Abstract
A self-attested log is not evidence. This note states precisely what our DSSE and in-toto attestation chain establishes — that no row was edited by anyone lacking the relevant signing key — and precisely what it does not: that the chain was never wholesale replaced by someone who holds those keys or controls the trust map. It explains why external transparency-log anchoring is the gap that separates the two, and why we do not use the phrase 'independently verifiable' before it ships.
Summary
The proposal, policy decision, approval, and execution events for a change are each signed and hash-linked into a chain. Verification re-runs on every read of an incident’s ledger rather than being cached, so a tampered row shows as broken the moment anyone looks — not when a scheduled check happens to run.
That gives a real property. It gives a narrower property than the words normally used to describe it, and this note exists to keep the two apart.
Design
Envelopes follow DSSE with correct pre-authentication encoding, wrapping in-toto Statements. Signing is Ed25519 through the standard library — no additional dependency. Four keypairs exist, one per role: agent, policy engine, human approver, and executor.
Verification checks four things, and the third is the one that makes it a chain rather than a set of signatures:
- Each envelope carries a valid signature from a key trusted for that role.
- Role separation holds — an agent-signed envelope cannot occupy an approval slot.
- Each entry’s signed
parent_hashequals the previous envelope’s own hash. Insertion, deletion, reordering, or edit breaks a signature, not merely a foreign-key reference. - Cross-references are consistent: an approved policy’s proposal hash must match the real proposal, and an execution’s applied-diff hash must equal what was proposed and approved, not something swapped in afterwards.
Denied proposals get a real chain too. A denied change never reaches git and has no blob-SHA-based diff hash to sign against, so the proposed-but-never-committed changeset is content-hashed directly. That chain terminates at the denial and never grows an execution event — a refusal is recorded as a decision that was reasoned about, not as the absence of one.
What verification establishes
| Claim | Supported? | Why |
|---|---|---|
| No row was edited by someone without the relevant key | Yes | An edit invalidates that entry's signature and every subsequent parent hash. |
| The recorded approval was produced by the approver role's key | Yes | Role separation is checked as part of verification, not assumed. |
| The executed diff equals the approved diff | Yes, for changes through this pipeline | Cross-reference check, against a hash recomputed from the provider's API. |
| The approval was made by a specific named person | No | One keypair per role, not per individual. The approver identity is a string the caller supplies. |
| The chain was never wholesale replaced | No | The rows and the trust map live in the same database. A holder of the keys could rebuild both. |
Trust boundaries
Two boundaries are currently weaker than the cryptography suggests, and both are configuration rather than credential boundaries.
The signing keys are split across two trust-domain secrets rather than one shared secret, and the separation is enforced at runtime: each service refuses to load the other domain’s role, and — tested directly, bypassing that guard — decrypting the approver key with the agent secret fails authenticated decryption. The wrong key genuinely cannot read the ciphertext.
But all four keys live in one table, and possession of an environment string is the entire authorization model, with no external check. Nothing structurally prevents both secrets reaching one process; that happened once, briefly, during initial setup. The correct sentence is that the agent process does not currently hold that credential. Not that it cannot obtain it.
Vocabulary, and why we police it
A security reviewer who catches loose language discounts every other claim on the page. So these are fixed:
- Tamper-evident is correct today. Tamper-proof is never correct, at any stage of this work.
- Independently verifiable is not correct until external anchoring exists, because independence is exactly what is missing.
- Consequentialis the policy engine’s term of art. We avoid “risky”, which implies detection rather than classification.
The independence claim is the core of the argument for this whole system, so this is not a cosmetic restriction. It gates the strongest version of what we can say.
Open work
There is one further operational constraint worth stating plainly: signing keys must be provisioned per environment before any incident can be processed. Signing is not best-effort. An unprovisioned key throws rather than silently processing an unsigned change.