Cryptographic Output Integrity¶
Non-repudiation and a tamper-evident audit trail for model outputs (OWASP ML09) — what finance and healthcare deployments require and what no accuracy-focused guardrail provides. Two composable controls:
- Tamper-evident ledger — an append-only hash chain over output digests. Stdlib only, always available, tenant-safe (digests, never raw payloads).
- Ed25519 signing — detached signatures a third party can verify with only
the public key. Needs the optional
cryptographybackend (pip install director-ai[crypto]).
Quick start¶
from director_ai import ProductionGuard
from director_ai.core.config import DirectorConfig
integrity = ProductionGuard(DirectorConfig()).output_integrity()
# Sign an output for non-repudiation.
signed = integrity.sign("The diagnosis is X.", {"tenant": "t1", "model": "factcg"})
integrity.verify(signed) # True (the public key travels inside `signed`)
# Record its digest in the tamper-evident ledger (no raw output retained).
integrity.record("The diagnosis is X.", {"tenant": "t1"})
integrity.verify_ledger() # True — chain intact
Signing (non-repudiation)¶
OutputSigner holds an Ed25519 key (generated, or reproducible from a 32-byte
seed loaded from a secret manager). sign() returns a SignedOutput carrying
the output, metadata, the detached signature, and the public key:
from director_ai.core.output_integrity import OutputSigner, verify_signed_output
signer = OutputSigner(seed=my_32_byte_seed)
signed = signer.sign("answer", {"request_id": "r-1"})
verify_signed_output(signed) # anyone with `signed` can verify
Verification fails if the output, the metadata, the signature, the algorithm, or
the public key has been altered — the signature binds all of them. Without the
cryptography backend, signing raises MissingCryptoBackendError with the
install hint; the ledger below still works.
Tamper-evident ledger¶
TamperEvidentLedger chains each entry to the previous one:
entry_hash = SHA-256(prev_hash ‖ payload_digest). Altering, removing, or
reordering any past entry breaks every subsequent hash, and verify() recomputes
the chain to detect it.
from director_ai.core.output_integrity import TamperEvidentLedger
ledger = TamperEvidentLedger()
ledger.append({"output_id": "o-1", "digest": "..."})
ledger.append({"output_id": "o-2", "digest": "..."})
ledger.verify() # True
TamperEvidentLedger.verify_entries(exported) # verify an exported copy
Only digests are stored, never raw payloads, so the ledger can be exported for an
external auditor without leaking interaction content. LedgerEntry.to_dict() and
SignedOutput.to_dict() are JSON-safe.