← All posts
Jun 2026 · Field notes · Netizen Labs

No one can read Röbel's votes alone anymore

Today Röbel's voting system crossed a line it can never cross back. Until this morning, the privacy of every secret ballot rested on a single assumption: trust the founder, and trust the one server that held the decryption key. As of 11:00 UTC, that assumption is gone. Reading a single Röbel vote now requires 3 of 5 citizens, in different places, with different keys, to cooperate. Between elections, the key does not exist anywhere at all.

This is the moment Röbel stopped being a deployment and became a reference implementation.

The leak we removed

Röbel runs private voting on MACI, the system that lets a town hold a secret ballot onchain: votes are encrypted, no one can prove how they voted, and the final tally is still publicly verifiable. MACI is brilliant, but it has one soft spot. A "coordinator" holds the key that decrypts ballots to compute the result. Whoever holds that key could, in principle, read how every citizen voted.

For a town, that is the whole game. A secret ballot that one operator can quietly unseal is not a secret ballot. Until today, Röbel's answer was "trust us, the key lives on a locked-down machine." That is a promise, not a guarantee.

What changed

We replaced the single coordinator key with a 3-of-5 federation, using a piece of cryptography called Shamir secret sharing. The decryption key is split into five shares, one for each holder of a Citizen-Attester credential. The math has a remarkable property: any two shares together reveal absolutely nothing about the key. Not "hard to compute" nothing. Mathematically nothing. You need at least three of the five to reconstruct it.

So the trust assumption changed shape entirely. It used to be:

Trust the founder and one server.

Now it is:

Trust that 3 of 5 named citizens, in different locations with different keys, will not all collude with the operator at the same time.

That is a categorically smaller thing to ask. And critically, the full key never sits on a server again. During a tally it is reconstructed for about ten minutes inside one process's memory, used to compute the result, and then wiped. Between tallies, it does not exist. There is nothing to steal.

It is live, and you can check it yourself

This is not a design doc. It ran end to end today:

Because the important parts live onchain, you do not have to take our word for any of it. Anyone can verify that the system's coordinator key matches the federated key, and that the only credentials able to hold a share belong to the five Attesters. Verifiability is the point.

The honest part

We caught ten separate bugs taking this from "works in theory" to "ran tonight." Null receipts from an RPC provider. A smart-wallet action that had to be decoded out of an event log. A library that quietly collided two fields with the same name. A timestamp format mismatch. A cross-origin rule that blocked share submission. A race that could orphan a session. We wrote all ten down, with the fixes, because the next town should not have to rediscover them.

That is what a reference implementation is. Not the version that looks clean in a demo, but the version that survived contact with reality and left a map behind.

Why this is the right shape

Netizen Labs exists to give communities sovereignty they can verify, not sovereignty they have to take on faith. A town running its own secret ballots should not have to trust a founder, a company, or a single machine. It should be able to trust math, a handful of its own citizens, and a public ledger anyone can audit.

As of today, Röbel can. That is the bar. Every garden we help grow after this one starts here.