Private by design
The option you pick is designed never to be a readable field in a transaction or a database. Receipts are hashes, designed so they don't show how you voted.
Open source · Not for profit · Self-hostable
ZK-Voting lets any group run its own elections and polls. When creating one, the operator chooses a poll or a vote and whether re-voting is allowed. That setting can't be changed afterward. Turnout is published on a delayed hourly schedule for both kinds. A poll also publishes per-choice counts on that schedule once 100 voters have cast their first ballot, or at the scheduled end, whichever comes first. A vote publishes choices only at the deadline. Zero-knowledge proofs are designed to show that only eligible people voted, each one once, and that the published tally matches the ballots (tests currently use a mock verifier). Individual choices are designed not to be published.
The big picture
Anyone can run a civic L2 for their own elections. Each one batches votes and settles them to Ethereum L1, so the record outlives any single operator.
hash(L2 ID, election UUID).Most elections still ask voters, candidates and courts to trust a few operators: who is on the roll, who was turned away, whether a ballot was counted, whether the result matches. ZK-Voting turns those claims into things anyone can check.
The option you pick is designed never to be a readable field in a transaction or a database. Receipts are hashes, designed so they don't show how you voted.
Registrations and disqualification codes are public. Turnout is published on a delayed hourly schedule for both kinds. A poll also publishes per-choice counts on that schedule once 100 voters have cast their first ballot, or at the scheduled end, whichever comes first. A published hourly block is designed to stay as first published. A vote's choices stay sealed until the deadline. An independent auditor is designed to be able to rebuild a published result without the operator's private database.
Every ballot carries a proof that the voter is on the frozen roll and not disqualified. The operator decides at creation whether re-voting is allowed, and that setting can't be changed afterward. If it is allowed, the last ballot before the deadline counts. If not, a second ballot is rejected.
Nothing a voter is given is designed to prove their choice to someone pressuring them. Choose a vote when a running choice count would add pressure. Turnout is published on a delayed hourly schedule, as in a poll. A poll also publishes per-choice counts on that schedule once 100 voters have cast their first ballot, or at the scheduled end, whichever comes first. The choices stay encrypted until the deadline. If the operator allowed re-voting at creation, a voter can replace their ballot until then.
Barring someone is a public, coded, on-chain change with a stated reason, not a silent database flag.
AGPL-licensed, run at cost, built to be forked and rebranded. Your group owns the node, the roll and the record.
Any body that needs a roll, a ballot and a result people will accept.
Run civic elections and referendums with nested districts, measures and articles. Observers follow turnout while the election is open and the choices after the deadline.
Hold leadership elections and contract ratification votes where members trust the count without trusting the officers.
Take board votes and member polls, including weighted and ranked-choice ballots, with an auditable outcome report.
Run student government and faculty votes with passkey sign-in and a ballot that's accessible on any device.
Quick yes/no, single-choice or multiple-choice polls for any group that wants a private ballot and an honest show of hands. Turnout is published on a delayed hourly schedule for both kinds. A poll also publishes per-choice counts on that schedule once 100 voters have cast their first ballot, or at the scheduled end, whichever comes first.
Follow registrations and disqualification codes on the public explorer, and verify an outcome report once that contest is allowed to show it. Turnout is published on a delayed hourly schedule for both kinds. A poll also publishes per-choice counts on that schedule once 100 voters have cast their first ballot, or at the scheduled end, whichever comes first. A vote publishes choices only at the deadline.
Zero-knowledge proofs let the system check facts about a voter without putting the ballot in the clear. What anyone can see, and when, depends on the kind the creator chose.
The creator chooses a poll or a vote. A poll publishes per-choice counts hourly, with a 30-minute delay, after a gate. A vote keeps choices sealed until the deadline. Both end with one final batch, a challenge window, and the final tally.
Hourly per-choice counts, 30 minutes delayed, after the gate.
Choices stay sealed until the deadline.
A poll publishes hourly results after the batch. A vote stays sealed and shows turnout only. The deadline, the challenge window, and the final tally are shared.
Members sign up with a passkey, wallet, email or ID. The roll stores an identity commitment, never an address or document number.
Officials commit the eligible set and any disqualifications for the election.
Each ballot proves eligibility. The operator decides at creation whether re-voting is allowed, and that setting can't be changed afterward. If it is allowed, the last ballot before the deadline counts. If not, a second ballot is rejected.
A poll publishes per-choice counts once 100 voters have cast their first ballot, or the scheduled end arrives, whichever comes first. Each hour publishes 30 minutes after it closes. An hour with fewer than 10 ballots merges forward until a block has at least 10. A vote publishes turnout on the same delayed hourly schedule and keeps choices sealed.
No more ballots are accepted. A poll's published hourly blocks are designed to stay as first published. If re-voting was allowed, replacement ballots show up only in the final tally. A vote's choices are designed to be decrypted only as an aggregate by k-of-n guardians. One final batch goes to L1 at the deadline, then a challenge window, then the final tally. Anyone can check that the published totals match the accepted ballots.
Your group runs its own node with Docker Compose, Coolify, Dokploy or CapRover, optionally kept private behind a Tailscale sidecar. Every node serves its self-hosting guides at /docs.
Day-to-day activity lands on the ZK-Voting L2, a purpose-built civic sequencer and prover that settles to Ethereum. Final tallies can be mirrored on Ethereum mainnet.
# run your own node
cp .env.example .env
docker compose up -d --build
curl -fsS http://127.0.0.1:3000/health
ZK-Voting is under active development. Tests use a mock verifier, the first proving path is in progress and the trusted-setup ceremony hasn't happened yet. Don't use it for a binding election today.
Passing a vote never auto-executes anything. Outcomes are verifiable; acting on them stays with the people.
community.zk-voting.org
The community forum is where people discuss real use cases, ask questions about running a node, and shape what gets built next.