Security

The Trust Boundary, Written Down: What's Trustless Here and What Isn't

"Is this trustless?" has one answer for the proof and a different one for the payment. Both answers are precise, not hand-wavy, and this is where they're written down together.

2026-09-01 · source: docs/THREAT_MODEL.md

The proof: trustless, end to end

A zkvm-stark proof — produced by prove_program, checked by verify_program — is a real STARK. Verifying it, whether locally via zkvm verify, over HTTP via /v1/verify, or via the verify MCP tool, requires no trust in whoever generated it. The test suite backs this with negative tests: rejects_tampered_result and rejects_tampered_program confirm a proof manufactured for one claim is rejected against a different one. This is the only trustless part of the system end to end. Everything past this point is about what happens once that proof needs to affect something outside a local process.

The on-chain path: an attested bridge, not a verifier

contracts/'s ProofOrchestrator pays a prover once a proof is "accepted," and what "accepted" means depends entirely on which IProofVerifier it's configured with:

Concretely, the trust assumption is: the attester key is not compromised, and whoever controls it always verifies locally before attesting, never on faith. The contract has no way to detect a violation of that — it checks that the attester's signature is present, not that the underlying math is sound. This is the same trust model a centralized sequencer has before its fraud/validity proofs go live: a known, single point of trust, acceptable because it's explicit and small, not because it's absent.

Key management, honestly

There is currently no key-management story beyond "a private key exists and someone runs cast send ... --private-key." scripts/onchain_demo.sh's Anvil test keys are demo-only. There's no in-place rotation function on the current contract — rotation means deploying a new AttestedVerifier with a new attester address. That's a real gap for anything running continuously, written down as a decision, not discovered later as a silent omission.

A single wrong number would make this section marketing instead of a threat model, so the claim is scoped tightly: the proof is trustless, the payment is not — stated in exactly those terms, not "mostly trustless" or "trustless by design."

When does this stop being true?

Only when a real cryptographic verifier replaces AttestedVerifier — porting FRI/Merkle/field-arithmetic verification into Solidity, or wrapping the STARK in a SNARK cheap enough to verify on-chain. Both paths, and why each is a substantial independent undertaking rather than a follow-up PR, are described in docs/ONCHAIN_VERIFIER.md.

Related: why the contract is named AttestedVerifier and not StarkVerifier, and what's real vs. pitched across the rest of the project.