Semaphore is a zero-knowledge protocol developed by Koh Wei Jie (and later maintained by the Privacy and Scaling Explorations team at Ethereum Foundation) that enables anonymous signaling with group membership proofs. The core primitive: a user generates an identity (a secret Ed25519-like keypair), registers their identity commitment (a public hash of their identity) into a group’s Merkle tree, and later signals without revealing which group member they are — while the nullifier mechanism prevents them from signaling twice for the same external context (preventing double-voting). Semaphore serves as infrastructure for higher-level applications: anonymous voting systems (MACI), anonymous account recovery, privacy-preserving credentials, and whistleblowing platforms. The protocol is implemented as Circom circuits (~32,768 constraints), a Solidity smart contract for on-chain group management, and a JavaScript/TypeScript SDK for easy integration. Semaphore v3 (2023) significantly reduced circuit complexity and improved developer ergonomics.
Core Protocol
Three Phases:
1. Identity Generation (off-chain, user’s device):
“`
identity_secret = randomly generated
identity_nullifier = hash(identity_secret, 0)
identity_commitment = hash(identity_nullifier, hash(identity_secret))
“`
2. Group Registration (on-chain):
- User (or admin) adds
identity_commitmentto group’s Merkle tree - Group tree stored in Semaphore contract
- Anyone can verify tree membership (commitment is public; identity is not)
3. Signaling (ZK proof off-chain, verification on-chain):
“`
Proof proves:
- I know secret corresponding to some commitment in the group tree
signal_hash = hash(my_signal)
nullifier_hash = hash(identity_nullifier, external_nullifier)
“`
Nullifier prevents double-signaling: Same external_nullifier (e.g., “vote round 1”) + same identity always produces same nullifier_hash → contract rejects if nullifier_hash already used.
Applications Built on Semaphore
| Application | Use Case |
|---|---|
| MACI | Anti-bribery voting with Semaphore identity |
| Crypt-Keeper | Browser ZK identity wallet |
| Privacy-preserving DAO | Anonymous voting for sensitive governance |
| Zupass | Event attendance credential (Zuzalu) |
| Worldcoin | Basis for World ID’s anonymous action scheme |
Signal Types
A “signal” in Semaphore can be anything:
- A vote:
signal = keccak256("yes_on_proposal_3") - A message:
signal = hash(message_content) - An attestation:
signal = hash(credential_claim) - Any arbitrary data
Related Terms
Sources
- “Semaphore: A Privacy Gadget Built on Ethereum” — Koh Wei Jie / Ethereum Foundation (2019). The original Semaphore paper — introducing the group membership signal primitive, nutlifier design, circuit construction, and example applications.
- “MACI: Minimal Anti-Collusion Infrastructure for Voting” — Barry Whitehat / Ethereum Foundation (2019). Introduction of MACI — an application layer built on Semaphore-like ZK identity that prevents voter bribery and coercion in on-chain governance by making votes cryptographically unverifiable to third parties.
- “Zupass: ZK Event Credentials and Semaphore Identity” — PSE / 0xPARC (2023). Documentation of Zupass — a ZK identity and credential wallet built on Semaphore, developed for the Zuzalu event — enabling attendees to prove event membership and unlock features without revealing their specific identity.
- “Semaphore v3: Upgraded Protocol and Developer Experience” — PSE (Privacy and Scaling Explorations / Ethereum Foundation) (2023). Documentation of Semaphore v3 — the refactored version with redesigned circuits (Circom 2.0-based), improved SDK, reduced constraint count, and new features for large-scale group management.
- “ZK Identity Primitives: From Semaphore to World ID” — Worldcoin Foundation (2023). Analysis of the evolution from Semaphore’s group membership primitive to more complex ZK identity systems — specifically how Worldcoin’s World ID builds Sybil resistance and unique humanity verification on top of Semaphore-compatible primitives.