Trust and security
Where trust still sits
Vouch is trust-minimised, not trustless. This page says exactly where, before anyone has to ask.
On this page
#The boundaries
| Party | Trusted for | Why that is acceptable |
|---|---|---|
| Holder’s device | Keys and proving | Keys never leave it. The prover links no network client; nothing is broadcast. |
| Zcash chain | The truth | Both anchor roots are rebuilt from public compact blocks, and nc_root is checked against lightwalletd. |
| Anchor table | Which roots are real | Anyone can re-derive every entry with pof-anchor. A wrong entry can only make honest proofs fail. |
| pof-verify | Checking | Open source, one implementation compiled natively and to WASM; every verdict can be re-derived. |
| pof-attest | Solana only | Signs verdicts for a chain that cannot read Zcash. Allowlisted, k-of-n capable, and anyone can run one. |
| Revocation list | Availability | A hash lock: only the holder knows the secret, so the list can drop an entry but never forge one. |
#The attestor, plainly
Verification of a Zcash fact by a Solana program goes through attestors. The program trusts its allowlist and threshold and nothing else. If enough allowlisted attestors lie together, the program believes them. That is the honest cost of today’s design; the threat model lists what each party could do.
It is mitigated by making attestors boring and plural: stateless, re-runnable against the same proof and public data, and combinable (2 of 3 is a configuration change). A Zcash light client on Solana removes the attestor for the anchor; verifying the Halo2 proof inside a zkVM and checking a Groth16 wrapper on Solana removes it for the proof. Both are on the roadmap.
#What a leaked proof exposes
That some holder cleared a threshold at a block height, for a named audience, and nothing else: not the balance, not which notes or how many (the five slots are always filled), not the addresses. The governance nullifiers are unlinkable to on-chain nullifiers. A proof bound to a Solana account is useless to anyone else on-chain.
#What this cannot do
- Prove where funds came from before they entered the shielded pool.
- Prove a negative: that a holder has nothing elsewhere.
- Stop a holder spending the funds right after proving. Proofs are point-in-time; the anchor says which point. A consumer has to ask again, as a lender asks for a new bank statement:
pof-creditdraws only while a line’s latest proof is fresh (24 hours by default, never past the proof’s expiry), and a refresh needs a new proof at an anchor no older than the line’s last or the pool’s floor. A holder who spent the funds cannot make one. - Stop the same notes backing proofs to several audiences. Each proof is single-use where it lands (one receipt, one credit line), but the funds are not locked.
- Prove more than five notes’ worth in one proof. A wallet with many small notes consolidates first.
- Make an institution accept a proof. That is a conversation, not a feature.
#The demo ledger
So that anyone can see a real proof without owning ZEC, the site ships a demo ledger: a real key, five real Ironwood notes, and a published tree and nullifier set that are ours, not mainnet’s. Every verdict against it says “demo anchor”. Mainnet anchors come only from pof-anchor.
#What we built on
The holding proof is the delegation circuit from voting-circuits (Valar Group), driven the way zcash_voting (Chainapsis) drives it, over the Zakura Orchard/Ironwood crates; all MIT / Apache-2.0. We added two circuit changes (the threshold, and the revealed nullifiers of exit certificates as a second circuit), a dense IMT, a precomputed-key loader, and a WASM build fix; each is listed in the vendored crate’s VOUCH-MODIFICATIONS.md.
What we built is the envelope and its binding to the circuit, the anchor indexer, the prover and verifier, the attestor, the Solana programs and this site. No audit and no formal verification have been done. This is not production-ready.