Expand description
Signing a verdict, and verifying one without asking anybody.
§Why a verdict has to be signed at all
An unsigned verdict is a row in a log file. Anyone who can write the log can
write “the tests passed”, and nobody reading it later can tell the
difference between a checker that ran and a line someone typed. That is the
whole gap between logging and evidence, and Law III sits on the far side of
it: a state change commits on Evidence, so evidence has to be worth
something.
ed25519 over the canonical CBOR encoding closes it. The signature covers the
verdict, the checker’s identity and version, the digests of exactly what was
checked, the key identity, and how the verdict was reached — everything
except the signature itself, which is what
Evidence::signing_payload returns.
Change any of it and the signature stops verifying.
§Deterministic signatures, deliberately
ed25519 derives its per-signature nonce from the private key and the message (RFC 8032 §5.1.6) rather than sampling one. Signing the same evidence twice therefore produces the same 64 bytes. That is load-bearing here rather than incidental: US-0801 requires a checker to be a pure function of its inputs, and replay (US-1006) re-runs a recorded check and compares. A randomised signature scheme would make every replayed run differ in a field that says nothing about whether the check agreed.
§Verification is a free function
verify takes evidence and a public key and returns an answer. No
runtime, no registry, no async, no I/O. A compliance reviewer with a ledger
file and a published key can check every verdict in it without running BoundGraph
at all — which is the only version of “auditable” that means anything.
§There is no path to unsigned evidence
sign_evidence is the only constructor in this crate, and it returns
Result. There is no unsigned(), no feature flag, and no fallback that
degrades to an empty signature when a key is unavailable. A signing failure
propagates and the step does not commit. That asymmetry is deliberate: a
blocked commit is an outage, an unsigned commit is a lie in the audit trail,
and only one of those can be fixed after the fact.
§Key management
Which keys exist, and who holds them, is unresolved — ARCHITECTURE.md §17
lists it as the one open question blocking M3. docs/evidence-keys.md
records the three candidate strategies and what each costs. This module is
written so that the decision can be made later without changing any caller:
Signis a trait, so an external KMS or an HSM is an implementation of it rather than a rewrite.LocalKeyis the in-process one, which is what M3 runs on.- The key identity travels in the evidence, not the key material, so a verifier looks the key up rather than trusting what it was handed.
KeyRingis the smallest possible resolver of identity to public key. A KMS-backed one satisfies the same call.
Structs§
- KeyRing
- Public keys, by the identity the evidence names.
- Local
Key - An ed25519 key held in this process.
- Public
Key - An ed25519 public key, as bytes.
Enums§
- Sign
Error - Signing failed, so nothing commits.
- Verify
Error - Verification failed, so the evidence proves nothing.
Constants§
- PUBLIC_
KEY_ LEN - How many bytes an ed25519 public key is.
- SEED_
LEN - How many bytes a private seed is.
Traits§
- Sign
- Whatever can produce a signature for a key it holds.
Functions§
- sign_
evidence - Build evidence and sign it, in that order, as one operation.
- verify
- Check evidence against a public key.