Case study · Röbel an der Müritz, Germany · Base mainnet
The town that votes in secret, onchain, and can prove it counted honestly.
Röbel is a 750-year-old town of about 5,000 people in northern Germany. Since spring 2026 it runs a civic app where verified citizens propose, discuss, and vote on local questions. Ballots are encrypted on the voter's phone, counted under zero-knowledge proofs, and the key that opens the digital ballot box does not belong to anyone. It exists only when three of five citizens decide together that it should, for about ten minutes, and then it is gone again. This is the story of shipping that system to production, including the parts that broke.
10
citizens verified, zero documents collected
9
secret ballots held onchain (Base mainnet)
34
encrypted votes cast, zero readable by any single party
3-of-5
citizen keyholders required to open the ballot box
13:59
minutes from third key share to published, proven result
100%
gasless for citizens via account abstraction
~$3
total gas per tally, proofs and verification included
$15/mo
entire coordinator infrastructure
Onchain numbers are read live from Base and refresh hourly. The pilot runs with a deliberately small test electorate while voting windows are compressed to one hour; the architecture is sized for the whole town.
Why a town, and why this town
Civic digitization usually means one of two things: a PDF portal, or a platform that knows exactly who clicked what. Neither is acceptable for the one mechanism democracies actually care about, the secret ballot. And the standard crypto answer, token voting in a DAO, fails a town in the opposite direction: plutocratic, pseudonymous, sybil-vulnerable, and public. A town needs the intersection nobody had shipped: one verified human, one vote, in secret, with a publicly checkable result.
Röbel became the pilot because the problem is real there. Small towns make decisions constantly (festivals, traffic, budgets for the youth club) and participation is structurally hard: the people most affected are at work when the council meets. The town already had our civic app (news, events, local businesses, an AI assistant). Governance became its hardest feature.
The stack in three layers
Everything below is open source and runs on Base. The three layers are independent: each is useful alone, and together they form what we are building toward as the Netizen Stack.
- Citizen verification. Proof of personhood and residency by social attestation, not document upload. Output: a soulbound NFT per citizen.
- Private voting. MACI (Minimal Anti-Collusion Infrastructure, from the Ethereum research community). Ballots are encrypted end to end; tallies are proven correct in zero knowledge.
- Federated trust. Our contribution: the MACI coordinator key, the one soft spot in the design, replaced by a 3-of-5 Shamir federation of citizens. No single party can ever read a vote.
Layer 1: verification without surveillance
Every governance system that matters starts with the same question: who is allowed to vote, and how do you know they are real? Röbel's answer deliberately avoids the two default paths. There is no document upload (nothing to leak, nothing to subpoena, nothing to sell), and there is no biometric hardware. Instead, verification works the way a small town actually works: people know each other.
A resident requests verification in the app. Two already-verified people must vouch onchain: one holder of an Attester credential (a small set of civically trusted residents) and one regular verified citizen. Both attestations are transactions; the contract enforces the quorum. The result is a soulbound, non-transferable Citizen NFT: one per person, revocable by the same two-key social process if someone moves away or a mistake is made, and governed by the town itself (attestation thresholds are parameters the DAO can change, not constants we control).
The trust assumption is explicit and human-scale: an attacker needs to corrupt two specific verified residents per fake identity, in a town where people notice. That is a much higher bar than "bought a verified account farm" and a much lower ceremony than eID integration. Crucially, the failure domain is local and auditable: every attestation is public, so a bad attester leaves a trail.
Layer 2: the secret ballot, for real this time
Voting runs on MACI v2. The properties, briefly, for those who have not worked with it: ballots are encrypted to a coordinator key before they touch the chain, so no observer can read them. Voters can override their own earlier vote, and (the anti-collusion trick) can secretly change their MACI key, which makes receipts unprovable: you can show a coercer a ballot, but you cannot prove it is the one that counted. The tally is computed off chain and accompanied by Groth16 proofs that the contract verifies before accepting a single number. Wrong counts do not revert to a dispute process; they simply cannot be submitted.
What we added is everything MACI does not specify: the part where a human being with no crypto literacy votes from a phone. Citizens get a smart account (ERC-4337) behind an email login; the town sponsors gas, so voting costs a citizen nothing and shows no wallet ceremony. Voting keys are derived deterministically from a wallet signature, which sounds like a detail until a citizen reinstalls the app and discovers their random key is gone. We learned that one in production.
The copy in that flow is the production copy, translated from the German the app actually ships, and it is doing the heaviest lifting in the whole system. Citizens do not read cryptography. The app speaks in the one ritual every German already trusts: the sealed envelope and the locked ballot box. "Sealed and cast. From now on nobody can see your vote. Not the town. Not us." Every one of those sentences is backed by math, and none of them mention it.
Layer 3: the breakthrough, nobody holds the key
MACI has one honest weakness, stated plainly in every serious writeup: the coordinator can read individual ballots. Not forge results (the proofs prevent that), but read votes. For a DAO this is an accepted trade-off. For a town it is disqualifying: a secret ballot one operator can quietly unseal is not a secret ballot, it is a promise.
So we removed the operator. The coordinator's decryption key is generated in a browser, split with Shamir secret sharing into five shares, one per Attester, each share sealed to a key derived from that citizen's own wallet signature. The plaintext key is wiped seconds after the split. The new public key is activated through an onchain governance proposal (proposed, voted under MACI, queued through a timelock, executed), so even the rotation itself is governed.
Before, standard MACI
One coordinator key in one server's environment variable. Whoever roots the box, subpoenas the operator, or simply is the operator can read every ballot, silently, forever.
After, federated MACI
Five citizens hold sealed shares. Any two reveal nothing, information-theoretically. Three must act together, in public, through a signed session, to reconstruct the key in memory for minutes. Between tallies the key does not exist anywhere.
When a voting window closes, a tally session opens. Each keyholder authenticates with their wallet (ERC-1271 for smart accounts, learned that in production too), decrypts their share locally in the browser, and submits it directly to the coordinator service over a manifest whose signature the browser verifies against the onchain coordinator address before sending anything. At the third share, the pipeline below runs unattended. This is a replay of the real thing:
After the first federated tally succeeded end to end, we deleted the legacy single key from the infrastructure and verified the old code path dies with an error. That deletion is the point of no return: from that moment, reading a Röbel ballot requires three citizens to conspire, and forging a count requires breaking Groth16. The town does not have to trust us, and neither do you: every result links to the verifier contract on Base.
The day the system refused to lie
The best evidence for this architecture is the worst day we had with it. After the key rotation, two consecutive polls came back with a tally of zero. Citizens had voted; we watched them. The dashboards said 0, 0, 0.
The cause was a client bug of exquisite irony: the mobile app had the old coordinator public key hardcoded, so it sealed every ballot to a key that no longer existed. The circuit, correctly, discarded every undecryptable envelope and then proved, in zero knowledge, that the honest count of valid ballots was zero. No error, no exception. Just a proven, public, painfully honest zero.
Sit with what did not happen. The system did not guess. It did not count unverifiable ballots to be helpful. It refused to fabricate a result, because fabricating results is the one thing it is mathematically incapable of doing. We fixed the client (the app now reads the coordinator key from the poll contract itself at vote time, so the bug class is gone), re-ran a real poll, and watched 2 For arrive onchain, decrypted by three keyholders, proven correct. Those two votes are the most audited votes in the history of Mecklenburg-Vorpommern.
Operations, honestly
The entire coordinator runs on one small cloud machine for about $15 a month. A full tally costs roughly $3 in gas on Base, takes about 14 minutes of which 10 are proof generation, and requires three humans to spend ninety seconds each in a browser. Everything else (chain watching, result publication, the pipeline dashboard the keyholders watch) is automated. One person operated the entire stack while building it. That is the actual unlock of this design: election-grade privacy at parish-budget cost.
What still costs real effort is exactly what should: the key ceremony (an afternoon, five people), and writing words normal citizens understand. The cryptography was the easy half. We published the full production runbook, the ceremony documentation, and the ten bugs we hit on the way, because the next town should not have to rediscover them.
Verify it yourself
Do not trust this page. The entire deployment is public on Base mainnet; every claim above can be checked against the contracts:
The fact band at the top of this page is not copy. It is read from these contracts at render time.
What Röbel proves
- Proof of personhood does not require documents or biometrics. Two neighbors and a contract are enough, and the failure modes are human-sized.
- MACI is production-ready for civic use, if you wrap it in account abstraction, deterministic keys, and language people trust.
- The coordinator problem, MACI's known soft spot, is solvable today with threshold cryptography and five committed citizens. No new primitives required.
- The whole thing runs at a cost any community on earth can afford.
Röbel is one town. The point was never one town. Every contract, workflow, and word of copy was built to be forked. That is the Netizen Stack, and it is what we are at Berlin Blockchain Week to talk about.
Read the field notes for the deeper technical writeups, or the thesis for where this goes next.