Transcript
The transcript is a list of JSON lines, each{seq, at, type, data}. The lines form an append-only keccak256 chain:
outputHash is keccak256 of the judge’s full output text.
Signature
The judge key is a secp256k1 key. It signs a 32-byte digest with the EIP-191 personal-message prefix. The digest depends on where the record anchors:- Base:
keccak256(abi.encode(uint256 chainId, address registry, bytes32 runId, bytes32 transcriptRoot, bytes32 outputHash, bytes32 hostdata)). - Starknet:
keccak256(abi.encode(uint256 chainIdFelt, uint256 registry, uint256 commitment)), wherecommitmentis a Poseidon hash over'CHANCE_RUN_V1'and the 128-bit halves ofrunId,transcriptRoot,outputHashandhostdata. See Private records.
runId is the value returned as requestHash on a verification. hostdata is the enclave’s code measurement, or 32 zero bytes when the run was not attested.
Attestation
When the judge runs in an attested enclave, the model calls, the transcript chain and the signing all happen inside an AMD SEV-SNP confidential VM. The key carries a Microsoft Azure Attestation token whosex-ms-sevsnpvm-hostdata claim is the code measurement. That measurement is bound into the signed digest and into the onchain record, and the token’s hash is committed in run.attested.
attested is set per run. true means the signing key’s attestation mode was Azure SEV-SNP. false means the run was signed outside an attested enclave. The receipt then still proves the transcript’s integrity and which key signed it, but not the environment that ran the judge. GET /api/status shows the current judge address and attestation mode.
Anchoring
After signing, the commitments are written onchain:- Base:
RunRegistry.recordRun(runId, transcriptRoot, outputHash, hostdata, uri, signature). The registry rejects signatures from keys that are not registered judges.uriis the transcript URL, which only the owner can open. - Starknet: the private registry. See Private records.
anchorTx is null. After a successful anchor, the harness polls the registry for about 10 seconds to confirm the record.
Receipt fields
POST /api/v1/intent (201) and verify_intent return proof with:
string | null
Head of the transcript chain.
string | null
keccak256 of the judge’s output text.
string | null
The judge’s EIP-191 signature over the digest.
string | null
Address of the signing key.
boolean
Whether the key’s attestation mode was Azure SEV-SNP.
string | null
Anchor transaction hash, or
null when anchoring was skipped or failed.boolean
Whether the registry confirmed the record.
string
https://harness.chance.cc/api/runs/{id}/transcript. Owner-only.string | null
Explorer link for
anchorTx: basescan.org for Base, voyager.online for Starknet.GET /api/v1/intent/{id} returns transcriptRoot, outputHash, signature, judge, attested and anchorTx, plus anchorStatus in place of anchorVerified, transcriptUri and explorerTx:
The MCP
get_verification tool returns the same fields as the GET, plus transcriptUri and explorerTx. hostdata and the digest are not in any API response. They are in the transcript JSON.
Check a receipt
As the owner. Openhttps://harness.chance.cc/verify/{id} signed in as the account that ran the verification. {id} is the path segment of transcriptUri between /api/runs/ and /transcript. The page loads the transcript and, in your browser:
- Verifies the attestation token’s RS256 signature against the issuer’s published keys, as of the time the transcript committed it. Checks that it attests a SEV-SNP VM that is not debuggable, that
hostdatais a published Chance enclave build, that the token names this judge, and that its hash matchesrun.attested. - Recomputes the transcript root from the lines.
- Checks keccak256 of the output against
outputHash. - Recovers the signer from the digest and signature and compares it with
judge.
GET {transcriptUri} with your API key returns the same transcript JSON.
As a third party. Transcripts are owner-only: the URL is published in the onchain record, so the URL alone opens nothing. The owner downloads the JSON from the verify page and shares it. The recipient can check it without Chance:
- Recompute the root from
linesand compare it withtranscriptRoot. - Compare keccak256 of
outputwithoutputHash. - Rebuild the digest from
chainId,registry,runId,transcriptRoot,outputHashandhostdata, recover the signer fromsignature, and compare it withjudge. - On the Base registry, call
judges(judge)andverifyRun(runId, transcriptRoot, outputHash, hostdata). Both should returntrue. - If
attestation.maaTokenis present, verify its RS256 signature against the keys at{iss}/certs, check thatx-ms-sevsnpvm-is-debuggableisfalse, and comparex-ms-sevsnpvm-hostdatawithhostdata.
anchorChain: "starknet"), steps 3 and 4 use the Starknet digest, with starknetChainId, starknetRegistry and commitment from the same JSON.
GET /api/status needs no authentication and returns the registry address, chain id, current judge address, attestation mode, model provider and model.
What a receipt does not prove
- That the decision was right. It proves what was judged, what the judge said, and which knowledge it used.
- That the input was true. A wrong intent or wrong context is faithfully recorded and judged as given.
- That the provider served the model it reported.
agent.model_callrecords the served model as the provider reported it.

