Protocol
How a dispute is settled without trusting anyone
Three problems have to be solved before an escrow for machine payments is worth anything: proving what was delivered, judging it reproducibly, and doing both without publishing the thing the buyer paid to keep private.
Problem one
Neither side can lie about what was delivered
This is where most escrow designs fall over. If the buyer supplies the disputed response, they can paste in garbage and claim a refund. If the seller supplies it, they can produce a perfect answer they never actually sent.
Seller signs
sig( paymentId ‖ keccak(body) )
Fixes the hash. Cannot deny it later.
Buyer submits
the body, and that signature
Supplies the bytes. Cannot swap them.
Enclave checks
keccak(body) == signed hash
Mismatch ends the case before merit.
The hash is fixed by the seller and the bytes come from the buyer, so a case is only judged on merit once the two agree. Neither party has to be trusted and no witness has to be paid.
A seller that withholds the signature gains nothing by it. The buyer opens a receipt challenge, the seller gets a grace period to produce the receipt, and silence refunds the buyer.
Problem two
The judge cannot be a language model
The instinct is to put a model in the enclave and ask whether the response looked right. That breaks the protocol. An enclave result is attested and verified by DON consensus, which requires the same input to produce the same output every time — and a model does not.
Keep it deterministic for a given input — the enclave result is attested and verified by DON consensus before the workflow completes.
So the model moves upstream, where non-determinism costs nothing. It helps a seller author a machine-checkable SLA: a set of assertions over the response, content-addressed and agreed before any money moves. The enclave then does something more defensible than judging — it enforces a contract both sides already signed.
{
"op": "numeric.lte", "path": "$.spreadBps", "value": 50,
"note": "spread within 50bps"
}Every operator is a pure function of the payload. Numeric comparisons refuse numeric strings, so a seller cannot ship the wrong type and pass. Patterns are anchored at both ends, because an unanchored pattern matches on substrings and that is nearly always the opposite of the promise intended.
Freshness is checked from two timestamps inside the signed body rather than against a clock, so the verdict cannot be skewed by how long a buyer waits before disputing.
Problem three
The rules are public, the data is not
Adjudication requires reading the disputed payload — exactly the thing a buyer paid to keep private. A trading signal, a credit decision, a medical summary. Publishing it to settle a fifty-cent argument is worse than eating the loss.
Sealed in the enclave
- Request and response bodies
- The SLA document
- Any vault secrets
- Which clause failed, and why
Crosses out on-chain
- paymentId
- outcome · APPROVE | REJECT
- reasonCode · uint16
- enclave attestation
One precision worth stating, because it is easy to claim the opposite: the workflow binary is handed to the enclave by the Workflow DON and is public. Confidentiality covers the data it computes over, never the logic. That is the right way round — the rules being auditable is the point, and a judge whose reasoning is secret is not a judge worth trusting.
Reading a verdict
A refund names the promise that broke
Verdicts settle with a uint16 reason code. Codes at or above 100 are a failed clause, and the index points at a specific published assertion — so a refund is attributable rather than a matter of opinion.
| Code | Meaning |
|---|---|
| 0 | Approved |
| 1 | Receipt mismatch |
| 2 | Malformed body |
| 3 | Content-Type mismatch |
| 4 | Missing required field |
| 5 | Latency exceeded |
| 6 | No verdict arrived |
| 7 | SLA mismatch |
| 8 | Unknown operator |
| 100 + n | Clause n of the SLA failed |
Reason 102 — the one from the live run — is clause 2: a positive-bid assertion, broken by a quote that came back negative.
Architecture
Two shapes forced by platform limits
CRE cannot write to Hedera
Of the 57 chains a CRE tenant can target, Hedera is not one — there is no forwarder for a report to reach. So the enclave writes its verdict to Ethereum Sepolia, where a forwarder verifies the DON signatures, and CCIP carries it the rest of the way. Every hop is authenticated: DON signatures on Sepolia, CCIP provenance on Hedera. No privileged operator sits anywhere in that path.
CRE cannot watch Hedera either
Nothing can subscribe to the dispute event, so the adjudicator sweeps a queue instead. That queue is untrusted by design. The enclave re-reads the payment from chain at a pinned block and verifies both hashes before judging anything, so a dishonest index can delay a verdict or waste a sweep — it can never change one.
A stalled dispute still resolves
If the workflow pauses, the relay runs dry, or a lane stalls, the payment would otherwise sit forever. After a timeout anyone can settle it and the buyer is made whole, bond included — they posted it in good faith and it is the adjudicator, not the buyer, that failed. Every state has a way out, including the one where the machinery itself breaks.