A department, program or co-op publishes its members’ spend keys. A receipt shows it was signed by a member, not which one.

Use

Each member prints their key:
The organisation publishes the lines as keys.txt. A member makes a receipt over the set:
A verifier names the set it expects:
A real testnet example, a four-wallet co-op, is in examples/testnet/keyset.

How it works

For a set K_1..K_n, every spend’s rk is K_j + [alpha]G for some j whose alpha the prover knows. It is a one-of-many Schnorr proof: special soundness extracts alpha for at least one j, and the simulated branches make every j look alike. So the signer holds one of the keys, or knows its relation to one. No circuit.

Limits

  • The set is whatever the verifier supplies. A verifier who accepts the wrong set learns the wrong thing; the page and CLI make you name it.
  • It hides which member, not that a member signed.
  • Distinct from FROST policy: a key set is n separate keys, a policy is one key held by n parties.
Code: crates/zceipt-core/src/keyset.rs. Test: roundtrip.rs::key_set_proof_names_the_set_not_the_key. The spec verifier checks conformance/keyset.json.