Zero-knowledge reputation

An agent proves “I hold a VALID Karatum certificate for web3 with tier ≥ B and K ≥ 18, not recalled, issued by Karatum” without saying which agent it is. Karatum publishes a Merkle root of its reputation tree on Base Sepolia and Ethereum Sepolia daily and on every recall; the agent proves membership with a Groth16 proof generated on its own machine; a contract checks the proof and the root's freshness. Testnets only.

Try it: prove in your browser, verify on Base Sepolia

Task class web3 (covers web3.swap). The agent's identity secret is public on purpose (demo agents); a real agent keeps it private. The proof is made on your device: the circuit (3.3 MB wasm) and proving key (3.8 MB) are downloaded once.

Recall: a recalled agent's proof fails after the next root

Recorded drill KAR-2026-0019 (2026-10-17 10:02:28 UTC), labelled DRILL in the incident registry. Every step is a transaction on Base Sepolia.

  1. accepted Agent B proves in zero knowledge and is accepted
    root epoch 1, block 48231501
    nullifier 0x049e15a55f…088c
  2. step DRILL incident KAR-2026-0019 confirmed and recalled
    KaratumIncidentLog announcement in block 48231501
  3. step Next root published immediately, without B's leaf
    epoch 2 in block 48231506 (immediate: the previous root stops counting in that block)
    5 blocks after the recall announcement on Base Sepolia
    both chains published 14.8 s after the recall run finished
  4. rejected B's proof made before the recall is rejected on chain
    ProofRejected(ROOT_NOT_ACCEPTED) in block 48231513
    B cannot make a new proof: its leaf is not in the new tree
  5. accepted Control: agent A proves against the new root and is accepted
    root epoch 2
  6. accepted Recall lifted: B is back in the next root and accepted again
    root epoch 3

What is hidden, what is not

ItemIn the proof?Why
Which agent (wallet, build, ERC-8004 id)hiddenThe proof shows membership in the tree, not which leaf.
Exact K-rating and tierhiddenOnly the thresholds the verifier asked for (K ≥ k, tier ≥ t) are proven.
Certificate expiryhiddenProven to be later than the proof time.
The identity secrethiddenNever leaves the agent; the server only ever saw Poseidon(secret).
Task classpublicPublic: the verifier asked for it.
Which root (so which day's tree)publicPublic: the verifier needs it to check freshness and recall.
Nullifier for this verifier's scopepublicA stable pseudonym per verifier: the same agent always gets the same value in one scope (one proof per agent per scope) and unlinkable values across scopes.
Proof timepublicPublic, to bound replay; rounded only by the prover's clock.

Limits, stated plainly: the anonymity set is the set of agents in today's tree that satisfy the same thresholds, and it is small today (the demo agents and few others). Karatum itself knows which wallets are in the tree (it builds it), though not which one made a given proof. The demo agents' secrets are public on purpose.

Contracts and roots

Base Sepolia

ZkRepRegistry 0x3B58b9…1f3a

Groth16 verifier 0xb79e19…1eb4

Safe guard accepting zk proofs 0xF1a12E…560E

Latest root: epoch 3, 3 leaves, change, published 2026-10-17 10:02:14 UTC

Ethereum Sepolia

ZkRepRegistry 0x9B5E60…68A9

Groth16 verifier 0xb79e19…1eb4

Safe guard accepting zk proofs 0x09072D…B6c3

Latest root: epoch 3, 3 leaves, change, published 2026-10-17 10:02:27 UTC

Recent proofs

WhenKindThresholdsNullifierRootResult
2026-10-17 10:02:28 UTCaccept
drill: agent B after the recall was lifted
tier ≥ A, K ≥ 220x1e20ca97…084e0x23a4217a…b71caccepted tx
2026-10-17 10:01:53 UTCaccept
drill: agent A (control) on the recall root
tier ≥ B, K ≥ 180x1259f8b8…be1f0x2236021d…2741accepted tx
2026-10-17 10:01:52 UTCreject
drill: agent B after recall (proof made before the recall)
tier ≥ A, K ≥ 220x2f35bc5f…ef6d0x24d866ae…0212ProofRejected(ROOT_NOT_ACCEPTED) tx
2026-10-17 10:01:28 UTCaccept
drill: agent B before recall
tier ≥ A, K ≥ 220x049e15a5…088c0x24d866ae…0212accepted tx
2026-10-16 17:59:48 UTCreject
demo:A
tier ≥ B, K ≥ 180x149bead7…8a100x15aa8dbf…963fNullifierUsed
2026-10-16 17:59:48 UTCaccept
demo:A
tier ≥ B, K ≥ 180x149bead7…8a100x15aa8dbf…963faccepted tx
2026-10-16 17:51:41 UTCaccept
drill: agent B after the recall was lifted
tier ≥ A, K ≥ 220x21cfb109…a13d0x15aa8dbf…963faccepted tx
2026-10-16 17:50:52 UTCaccept
drill: agent A (control) on the recall root
tier ≥ B, K ≥ 180x2aa04d5b…5a9e0x19e7d99a…1336accepted tx
2026-10-16 17:50:52 UTCreject
drill: agent B after recall (proof made before the recall)
tier ≥ A, K ≥ 220x23d83ba5…84790x014d5348…3ad3ProofRejected(ROOT_NOT_ACCEPTED) tx
2026-10-16 17:49:26 UTCaccept
drill: agent B before recall
tier ≥ A, K ≥ 220x15a529ad…77b80x014d5348…3ad3accepted tx
2026-10-16 17:48:04 UTCaccept
drill: agent B after the recall was lifted
tier ≥ A, K ≥ 220x0f2411ed…1c650x014d5348…3ad3accepted tx
2026-10-16 17:47:28 UTCaccept
drill: agent A (control) on the recall root
tier ≥ B, K ≥ 180x0fe0df94…5edc0x193e8c0b…4e3baccepted tx
2026-10-16 17:47:26 UTCreject
drill: agent B after recall (proof made before the recall)
tier ≥ A, K ≥ 220x20475190…fbc50x253516fa…b1d5ProofRejected(ROOT_NOT_ACCEPTED) tx
2026-10-16 17:47:10 UTCaccept
drill: agent B before recall
tier ≥ A, K ≥ 220x2a6ef8bb…1e390x253516fa…b1d5accepted tx
2026-10-16 17:46:56 UTCguard
guard:demo-A
tier ≥ B, K ≥ 180x18347b69…fed20x253516fa…b1d5accepted tx

How it works

  • Leaf = Poseidon(idCommitment, taskClass, tier, K, expiresAt, VALID). idCommitment = Poseidon(secret), registered once with a wallet signature.
  • Circuit: Circom 2.2.2, 5903 constraints, depth-20 Poseidon tree, range checks on tier, K and expiry, nullifier Poseidon(domain, secret, scope).
  • Trusted setup: PSE Perpetual Powers of Tau, contribution #80, 2^13 for phase 1; phase 2: Karatum phase-2 contribution, Final beacon: Ethereum mainnet block hash (block 26206125 0x601eb12cbfcd08d7c6ebe2f8a7ab2809e9a9e860cb5eb9cf8ad4f2e2ffa27877).
  • Freshness: only the current root is accepted, plus the previous one for one hour after a routine publication; a recall publishes with immediate = true, ending that grace in the same block.
  • Trust Handshake zk mode: a new presentation type karatum-zkrep+groth16 answers the same challenge as an SD-JWT presentation, without the identity.
  • Guard: a Safe guard deployed with ZkRepRegistry as its registry gates a pseudonymous executor the agent bound in a proof, at the proven tier.
import { ZkRepClient, identityFromSignature, identityMessage, proveReputation, onchainScope, messageField } from "@karatum/zkrep";

const api = new ZkRepClient("https://karatum.com");
const id = identityFromSignature(await wallet.signMessage({ message: identityMessage(wallet.address) }));
await api.register(wallet, id);                       // once; enters the next root
const w = await api.witness(wallet, id, "web3");       // path computed locally from the public leaf list
const proof = await proveReputation({ identity: id, opening: w.opening, path: w.path, root: w.root,
  minTier: 2, minKarats: 18, nowTs: Math.floor(Date.now() / 1000),
  scope: onchainScope(84532, REGISTRY, myContract, APP_SCOPE), messageHash: messageField("order #42"),
  wasm: "https://karatum.com/v1/zkrep/artifacts/karatum_rep.wasm", zkey: "https://karatum.com/v1/zkrep/artifacts/karatum_rep_final.zkey" });

Method and trusted-setup notes · circuit · ZkRepRegistry.sol · API