§ 01
Problem
A safety claim about an AI release is only as good as the evaluation behind it. Publishing a red-team suite burns it: prompts leak into training data, get patched individually and stop measuring anything, and exploit traces become working material for attackers. Keeping the suite secret leaves the public trusting the press release.
CLOSED BOOK gives a third option: publish a verdict that is cryptographically bound to the exact build and the exact suite, and keep the evidence closed.
§ 02
Threat model
- Evaluator dishonesty
- Not prevented. The evaluator is trusted for the truth of the results. The evaluator key makes every attestation attributable, and the evidence commitment lets an auditor later check the private record against what was attested.
- Model-version mismatch
- Prevented. The circuit asserts the private model digest opens the public model commitment. An attestation for build A cannot be presented as one for build B.
- Suite-version mismatch
- Prevented. The suite digest and salt must open the published suite commitment. Editing the suite after publication breaks the binding.
- Tampered result
- Detected. Results are bound into the evidence commitment. A different result set does not open it.
- Replay
- Prevented per release. The circuit allows one attestation per (model, suite, predicate), keyed in the ledger's releases map; a fresh evidence salt changes the id but not the release, and is refused. A newly committed suite is a new release.
- Disclosure
- Minimised. Only values wrapped in disclose() reach the ledger: commitments, predicate, evaluator key and id. Salts stop low-entropy data from being brute-forced from commitments.
- Fake frontend verification
- Addressed by receipt states. Recomputing hashes earns only CLAIMED PASS, because anyone can build a self-consistent record. DEMO PASS and LOCAL CIRCUIT ATTESTED require the record's own issuer to hold it field for field; NETWORK VERIFIED requires the deployed contract to hold the record field for field, which needs a deployment (not done yet).
Full analysis: docs/THREAT-MODEL.md
§ 03
Public data
Public · on the ledger
- model commitment
- suite commitment
- predicate id + threshold
- evidence commitment
- evaluator public key
- attestation id
- release key
Private · witness only
Private values are redacted.
The verdict is implied by the existence of a record: the circuit can only produce PASS. There is no public FAIL record, because a published failure leaks information about the suite and invites targeted probing.
§ 04
Private data
- Sealed test casesThe red-team prompts and scenarios.
- Model outputs and exploit tracesWhat the model did under test.
- Individual check resultsSix booleans, supplied as a witness, never disclosed.
- Evaluator notesNever hashed, never committed.
- Salts and evaluator secretWitness inputs that make commitments hiding and attestations attributable.
- Unpublished thresholdsWhere a policy keeps internal limits private, they stay inside the evaluation.
§ 05
Commitments
H is Compact’s persistentHash over a vector of 32-byte words, which is SHA-256 of their concatenation. Every domain tag is pad(32, …). The TypeScript library in src/lib/commitments reproduces each value byte for byte; the test suite checks it against the compiled contract.
- evaluatorKey
- H("closedbook:evaluator:v1", secret)
- modelCommitment
- H("closedbook:model:v1", SHA-256(canonical(model manifest)))
- suiteCommitment
- H("closedbook:suite:v1", SHA-256(canonical(suite manifest)), suiteSalt)
- evidenceCommitment
- H("closedbook:evidence:v1", model, suite, predicate, pack(results), evidenceSalt)
- attestationId
- H("closedbook:attestation:v1", model, suite, predicate, evidence)
- releaseKey
- H("closedbook:release:v1", model, suite, predicate)
pack(results) sets bit i when check i passed, little-endian in one 32-byte word. Salts are 32 random bytes. A fresh evidence salt is drawn for every attestation.
§ 06
Release predicate
The predicate is public and fixed when the contract is deployed: an identifier and a threshold in the ledger. Safety Baseline 1 requires all six checks to pass — prompt injection, secret exfiltration, PII leakage, tool abuse, policy bypass and unsafe escalation.
export ledger predicate: Bytes<32>; // pad(32, "safety-baseline:1") export ledger threshold: Uint<8>; // 6 assert(countPasses(results) >= threshold, "release predicate not satisfied");
§ 07
Attestation
The evaluator calls one circuit, attest(model, suite). Private inputs come from witnesses. The circuit asserts five things, then records the attestation. If any assertion fails, no proof can be generated and nothing is recorded.
export circuit attest(model: Bytes<32>, suite: Bytes<32>): Bytes<32> {
const signer = deriveEvaluatorKey(evaluatorSecret());
assert(signer == evaluator, "not the registered evaluator");
assert(commitModel(modelDigest()) == model, "model commitment mismatch");
assert(commitSuite(suiteDigest(), suiteSalt()) == suite, "suite commitment mismatch");
const results = checkResults();
assert(countPasses(results) >= threshold, "release predicate not satisfied");
const release = deriveReleaseKey(model, suite, predicate);
assert(!releases.member(disclose(release)), "release already attested");
const evidence = commitEvidence(model, suite, predicate, packResults(results), evidenceSalt());
const id = deriveAttestationId(model, suite, predicate, evidence);
releases.insert(disclose(release), disclose(id));
attestations.insert(disclose(id), disclose(Attestation { ... }));
attestationCount.increment(1);
return disclose(id);
}Every disclose() in the contract is a deliberate, reviewable disclosure. The compiler refuses to put witness-derived data on the ledger without one. Full contract source
§ 08
Verification
- 01Find the record. Look up the attestation id on the contract ledger (or open a receipt link, which carries the public record).
- 02Recompute the id. Hash the public fields. A match means the record is self-consistent — nothing more. Anyone can build a self-consistent record, so this alone never makes a verdict verified.
- 03Check the bindings. Compare the model commitment with the one the developer published for the release, and the suite commitment with the evaluator's published suite commitment.
- 04Check the key. Confirm the evaluator key is the independent evaluator you expected.
- 05Check the proof. On a Midnight network, the transaction's zero-knowledge proof is verified by the network when it is accepted. The demo and local modes do not produce one, and say so.
Auditors can go one step further. Given the private opening — the six results and the evidence salt — anyone can check it against the public evidence commitment. A tampered result set does not open it.
§ 09
What CLOSED BOOK proves
- The holder of the registered evaluator key produced this attestation.
- They knew a model digest that opens the public model-build commitment.
- They knew a suite digest and salt that open the public suite commitment.
- They held six check results, committed in the evidence commitment, that satisfy the public release predicate.
- No attestation existed yet for this release: this model build, this suite, this predicate.
§ 10
What it does not prove
- That the model was executed, or executed correctly. The model never runs inside a circuit.
- That the evaluation was actually performed, or that the results are true. The evaluator is trusted for its inputs.
- That the suite is good, complete, current or representative of real-world risk.
- That the build deployed in production is the build that was evaluated. That needs a separate deployment attestation.
- That a model is safe. It proves a stated release condition was met on a stated suite.
Trust the attestation, not the press release — and know exactly what the attestation says.
§ 11
Implementation status
| Mode | What runs | Status |
|---|---|---|
| DEMO ADAPTER | TypeScript mirror of the circuit, in the browser. Real commitments, no proof. | Working |
| MIDNIGHT · LOCAL CIRCUIT | The compiled Compact contract (0.31.1) executed via compact-runtime 0.16.0 on the evaluator’s machine. No proof. | Working locally |
| ZK PROOF (OFFLINE) | Real proof of one attest call from the compiled circuit: proving keys from CI, official proof server 8.1.0 and WASM prover. 5/6 cannot be proven. Not bound to a transaction. | Generated |
| MIDNIGHT · NETWORK | Proven transaction on a Midnight network via proof server and wallet. Scripts and the receipt verifier exist; deployment waits on a funded wallet. | Not deployed |