Semaphore: proving group membership without revealing who you are
A zero-knowledge protocol for anonymous interactions.
At a glance
- What is it?
- Semaphore is a zero-knowledge protocol packaged as a set of npm packages, Circom circuits and Solidity contracts, where a user proves they belong to a group and can sign a message, endorsement or vote without revealing their identity. This looks at the package layout, the build, and the trust assumptions the design carries.
- Who is it for?
- Semaphore fits a team that needs to prove membership of a group, such as a token holder, a contributor list or a verified subscriber, and wants that proof to be checkable by anyone without revealing the member.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 21 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 6, 2026, and from our analysis. They are not legal advice.
Star history
- semaphore-protocol/semaphore1,096 ★ · +2
| Repository | 2026-10-20 | 2026-11-06 |
|---|---|---|
| semaphore-protocol/semaphore | 1.1k | 1.1k |
2 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
A generic privacy layer, stated in the project's own words
The one-line summary in the readme is the most useful description of the project: Semaphore is a generic privacy layer, and leveraging zero-knowledge technology, users can prove their membership in groups and send messages, extending from votes to endorsements, off-chain or across EVM-compatible blockchains, all without revealing their personal identity.
Three things in that sentence set the scope. The word generic matters, because the protocol is not tied to one application. Voting is the origin of the design, and endorsements are the extension of it, but the primitive underneath is a proof of membership plus a signature over an arbitrary message.
The second is off-chain or across EVM-compatible blockchains. The proof can be generated and checked anywhere, and the message can be delivered through a channel the protocol does not control, with the proof attached. That is the design that lets a group communicate without a server that knows who they are.
The third is what the protocol does not claim. It proves membership, not entitlement. A member of a group can prove they are a member; whether the message they sign is allowed, whether the action they request is legitimate, and whether the group itself is fairly constituted are all questions the protocol leaves open.
The site is semaphore.pse.dev, the licence is MIT, the primary language is TypeScript, and the topics list names the toolchain explicitly: circom, solidity, zk-snarks and proof-of-membership, with Ethereum and a monorepo layout.
The package set, from core to groups
The readme documents the project as a set of npm packages, and the table is the clearest map of the architecture.
`@semaphore-protocol/core` is the base, and the readme says the core of the protocol is in the circuit logic, with the diagram at `/packages/circuits/scheme.png`. `contracts` is the Solidity side, and the readme explains that Semaphore provides Solidity contracts and JavaScript libraries to make the steps for off-chain proof creation and on-chain and off-chain verification easier. `identity` is the package for the identity side, and its API documentation is linked from the table.
`group` follows, and the table is truncated at that entry in the readme, but the shape is clear from the directory listing: the repository holds `packages/circuits/` and `packages/contracts/`, and the packages directory at the root contains the published set.
That division is the practical way to think about the project. The circuits are the protocol proper, the contracts are the on-chain verifier, and the libraries are what an application developer actually installs. A developer wiring Semaphore into a product takes the JavaScript packages and, if they want the verification public, deploys the contracts.
The API reference is published separately at js.semaphore.pse.dev, and there is a chat and support channel linked from the readme as well as a contributing guide and a code of conduct.
One build command covers libraries, subgraph, website and docs
The root `package.json` describes a monorepo where the build is four builds, and the script names make the structure obvious.
"build": "yarn build:libraries && yarn build:subgraph && yarn build:website && yarn build:docs"`build:libraries` runs the build in every workspace package that is not private, `build:subgraph` builds the subgraph for the sepolia network, `build:website` builds the website workspace, and `build:docs` builds the docs workspace. The website and the documentation are part of the same build, which is a reminder that for a protocol project the documentation is a deliverable rather than an afterthought.
The test script is equally broad, running the circuit tests, the contract tests, the library tests and the subgraph tests concurrently:
"test": "concurrently -g -c auto -n circuits,contracts,libraries,subgraph \"yarn:test:circuits\" \"yarn:test:contracts\" \"yarn:test:libraries\" \"yarn test:subgraph\""The library tests are Jest with coverage, the contract tests run through the contracts workspace with coverage, and the circuit tests run in the `@semaphore-protocol/circuits` package. Linting is split the same way, with ESLint over the JavaScript and TypeScript and solhint over the Solidity, and formatting is Prettier plus embark, which is the documentation tool used for the docs workspace.
The rest of the tree supports that: `apps/` for the website, docs and subgraph applications, `packages/` for the published libraries, `types/`, `scripts/`, a `jest.config.ts` at the root, `typedoc.js` for the API reference, and a `.commitlintrc.js` with husky and lint-staged, so the commit message format and the pre-commit checks are enforced rather than suggested. There is a `changelogithub.config.json` for the changelog generation and a `CONTRIBUTING.md`.
The local environment, and what the private key is for
The `.env.example` at the root is short and tells you how local development is set up.
DEFAULT_NETWORK=hardhat
REPORT_GAS=false
BACKEND_PRIVATE_KEY=
COINMARKETCAP_API_KEY=
ETHERSCAN_API_KEY=The default network is `hardhat`, a local development chain, so the contracts can be deployed and exercised without touching a public network. The `BACKEND_PRIVATE_KEY` is the key of the account the deployment tooling uses, and it is empty in the example because it is yours to fill in. The `REPORT_GAS` flag switches gas reporting on and off for those runs.
The two API keys are for the parts of the toolchain that talk to public services: Etherscan for contract verification, and CoinMarketCap for the price data used by the token or group templates. Neither is needed to build the packages, and the readme's important note in this area is that the project is developed under the PSE acceleration programme, which is where the roadmap and grant funding for the ecosystem comes from.
The `.husky/` directory, the `.lintstagedrc.json` and the `.soldeerignore` file fill out the picture of a repository that runs its own dependency pinning for Solidity libraries, which is a common pattern in projects with a Circom and Hardhat toolchain where the Solidity dependency tree is not managed by npm.
Where the anonymity actually comes from, and where it stops
The honest way to evaluate a proof-of-membership protocol is to ask what is hidden and what is merely unlinked.
Semaphore hides the identity of the member. It does not hide the message, and it does not hide the group. A proof is generated against a group identity commitment held by a group organiser, and verification checks that the prover is in the group. The anonymity of the protocol is therefore bounded by the anonymity of the group's identity list: if the organiser knows which commitment belongs to which person, the organiser can still connect a proof to a member, even though nobody else can.
That is not a flaw in the protocol, it is the shape of the problem, and it is the first question to ask before adopting it. If your group is a list of email addresses held by a central party, proving membership with Semaphore is more machinery than the situation needs, and the party that issued the identities still knows who is in the group.
The second limit is cost and placement. A proof generated off-chain can be verified on-chain by a contract, which makes the verification public and permanent, or it can be verified off-chain, which is cheaper and private but means somebody trusted is checking. The readme's framing of off-chain proof creation with on-chain and off-chain verification covers both, and the choice is a deployment decision rather than a library feature.
The third is that the protocol proves membership, not permission. A member of a group can sign anything, and if the group is a subscribers list then membership is the right check, and if the group is an access control list then proving membership tells you nothing about what the member is allowed to do.
Where Semaphore is the wrong tool, and what the release line looks like
Three limits are clear.
The first is the toolchain. Circom circuits, a proof toolchain, a Solidity verifier and a Hardhat network are all part of the path. A team that wants to check a membership claim and does not already have a zero-knowledge build pipeline is signing up for a substantial setup, and the `build` script touching libraries, subgraph, website and docs is a hint of how much surface that is.
The second is the alternative that is usually better. If a server that holds the membership list can simply check a list, and the people trusting the result trust that server, then a proof of membership adds cost and complexity without removing a trust assumption. The protocol is the right answer when the checker and the group organiser are different parties, or when the check must be public and verifiable by anyone, and the wrong answer everywhere else.
The third is the content. If the requirement is to hide what was said rather than who said it, a proof of membership does nothing for you, and the readme's own wording is careful that the protocol sends messages and endorsements and votes, not private content.
On maintenance, the record is regular within the year. v4.14.1 was published on 2026-01-19, v4.14.2 on 2026-01-23, and v4.14.3 on 2026-07-08, with the last push to the repository on 2026-09-18. The version is still a 4.x line, so the API is expected to move, and the packages are versioned and released separately from the contracts, which is worth remembering when you pin one and not the other.
The licence is MIT, with the file at the root of the repository, and the project is run under the Privacy and Scalability Explorations organisation, which is the umbrella that also explains the PSE acceleration programme the readme points to.
Editorial conclusion
Semaphore fits a team that needs to prove membership of a group, such as a token holder, a contributor list or a verified subscriber, and wants that proof to be checkable by anyone without revealing the member. It does not fit a case where the group operator already controls the server and can simply keep a list, since a zero-knowledge proof buys nothing there, and it is the wrong tool if you need to hide the content of what is being signed rather than the identity of the signer. Before you adopt it, read how your group identities are issued, because the anonymity of the protocol is bounded by the anonymity of the group membership list, and check the gas cost of on-chain verification against the off-chain path.
Frequently asked questions
What does the Semaphore protocol actually prove?
That a user is a member of a group, and it lets that user sign a message, vote or endorsement with that proof attached, without revealing their personal identity. The proof can be created off-chain and verified on-chain or off-chain, across EVM-compatible blockchains.
Which packages does Semaphore publish?
The readme's table lists @semaphore-protocol/core, which holds the protocol's circuit logic, @semaphore-protocol/contracts for the Solidity verifiers, @semaphore-protocol/identity for identities, and a group package. The repository also holds packages/circuits and packages/contracts directories directly.
How do I build the Semaphore monorepo?
The root build script runs four builds in sequence: build:libraries for the published packages, build:subgraph for the sepolia subgraph, build:website and build:docs. The test script runs the circuit, contract, library and subgraph suites concurrently, with Jest and coverage for the libraries.
What environment variables does local development need?
The `.env.example` sets DEFAULT_NETWORK to hardhat, REPORT_GAS to false, and leaves BACKEND_PRIVATE_KEY, COINMARKETCAP_API_KEY and ETHERSCAN_API_KEY empty for you to fill in. The default network is a local chain, so the contracts can be deployed without a public network.
Is Semaphore suitable when the group owner already knows who the members are?
Only if the check needs to be public and verifiable by anyone. The anonymity of the protocol is bounded by the anonymity of the group identity list, so if the party that issued the identities can connect them to people, a proof removes nothing for that party and adds proof generation and verification cost.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/semaphore-protocol-semaphore)Star history chart for your README
[](https://hysenlabs.com/projects/semaphore-protocol-semaphore)Was this useful?