Confidential governance,
documented end to end.
QUORUM is confidential governance infrastructure for Solana organizations. Private inputs, public proof. This guide explains how each module works, what runs in your browser, what runs on the server, and what is settled on chain.
Overview
QUORUM lets a DAO vote, propose, and manage a treasury without leaking the information that makes governance manipulable. Ballots are encrypted in your browser, eligibility is proven with zero knowledge, the running tally stays hidden until close, and the final result is verifiable by anyone. Proposals stay sealed until execution, so there is no front running window. Treasury records stay confidential, with solvency proven by range proof and selective disclosure to auditors.
The protocol is organized into modules, each backed by real cryptography rather than a placeholder:
| Module | What it provides |
|---|---|
| Voting | Sealed ballots, ZK eligibility, threshold tally, verifiable correctness. |
| Proposals | Hidden until execution, commit reveal, on pass or timelock. |
| Treasury | Confidential balances, ZK solvency proofs, auditor disclosure. |
| Members | Eligibility snapshot and private delegation of voting weight. |
| Staking | Real Token 2022 QRM staking that settles on chain on devnet. |
| Proofs | A public surface where anyone verifies the protocol was honest. |
Architecture
The frontend is a TanStack Start React application. The governance API is a set of typed server functions, not a separate service, each validated with zod. Persistent state lives in Supabase Postgres. Cryptographic settlement happens on Solana devnet through a single consolidated program.
Browser (React, client side crypto + in browser ZK proving)
| Sign In With Solana, sealed ballot, proof generation
v
Server functions (createServerFn, zod validated, session JWT)
|
+--> Supabase Postgres commitments, nullifiers, proofs, encrypted records
+--> Solana devnet program "quorum_anchor": eligibility root, tally hash,
on chain Groth16 verifier, QRM Token 2022 stakingA deliberate design choice keeps cost near zero: the chain stores roots and hashes, never per ballot accounts. All ballots and commitments live in Postgres, and a single Merkle root per vote plus the final result hash are anchored on chain.
src/fn. Client crypto lives in src/lib/crypto. Solana helpers live in src/lib/solana. The on chain program is under onchain/.Wallet Authentication
Authentication proves wallet ownership without a password. The client requests a nonce, the wallet signs a canonical message, and the server verifies the signature with tweetnacl and issues a session cookie. QUORUM never asks for a seed phrase and never takes custody.
// client
const { nonce, message } = await requestNonce({ data: { wallet } });
const signature = await wallet.signMessage(message);
await verifySignature({ data: { wallet, nonce, signature } });
// server sets an HttpOnly session cookie; getMe() restores the sessionNonces are single use and expire, which prevents replay. Ballots are never linked to the session: the session gates spam, but the stored ballot is anonymous by construction.
Sealed Ballot Voting
A voter proves eligibility with a zero knowledge proof, encrypts the choice in the browser, and submits a sealed ballot. No one, including QUORUM, can read the choice until the tally runs. The running count of ballots is public, the choices are not.
1. Encrypt the choice. The vote is one hot encoded over the option set and encrypted with exponential ElGamal over Ristretto255. Because the scheme is additively homomorphic, encrypted ballots can be summed and only the totals decrypted.
2. Prove eligibility. The browser generates a Groth16 proof of membership in the eligibility Merkle tree plus a unique nullifier, without revealing identity or balance. The nullifier prevents double voting.
3. Tally with a threshold key. Decryption uses a 3 of 5 threshold key produced by a Pedersen DKG, so no single party holds the secret. The server sums the ciphertexts and recovers only the per option totals.
4. Prove the tally is honest. Each partial decryption carries a Chaum Pedersen DLEQ proof. Anyone can verify that the announced totals are the honest decryption of the sealed ballots, with no trust in the server.
| Server function | Purpose |
|---|---|
| castBallot | Verify the ZK proof against the live root, store the nullifier and ciphertext. |
| runTally | Sum ciphertexts, threshold decrypt, write totals plus a DLEQ transcript. |
| verifyTally | Re verify the DLEQ transcript so totals are provably correct. |
Hidden Proposals
Proposals are submitted encrypted. Only their existence, author eligibility, and voting rules are public. The content is revealed at execution or after a timelock, which removes the front running window entirely.
The body is encrypted in the browser with AES GCM and bound by a SHA 256 commitment. Only the ciphertext and the commitment reach the server. The author keeps the plaintext and reveals later, at which point the server checks the plaintext against the commitment.
const { encPayload, commitHash } = await sealProposal(text); // client side
await submitProposal({ data: { encPayload, commitHash, reveal: "on_pass" } });
// later, only the author can reveal:
await revealProposal({ data: { proposalId, plaintext } }); // commit is verifiedReveal policy is either on_pass, released once the vote passes, or timelock, released after a set window. The server rejects a reveal whose plaintext does not match the commitment.
Confidential Treasury
Treasury holdings stay confidential while remaining accountable. Solvency is proven with a zero knowledge range proof, and individual records can be disclosed to a chosen auditor without exposing the rest of the treasury.
Solvency proofs. The operator enters the real reserve balance and a public threshold. The browser generates a Groth16 range proof that reserves are at least the threshold, and only the proof and a commitment leave. The balance stays hidden. The proof is also verified on chain by the program.
Selective disclosure. A single record is encrypted to one auditor public key with ECIES over Ristretto255. Only the holder of that auditor secret can read it.
| Server function | Purpose |
|---|---|
| recordSolvencyProof | Store a ZK range proof of reserves, optionally verified on chain. |
| issueDisclosure | Encrypt one record to an auditor public key (ECIES). |
| addAuditor, revokeAuditor | Manage the auditor keys authorized for disclosure. |
Members and Delegation
Eligibility is committed as a zero knowledge Merkle snapshot. Members can vote, or privately delegate their voting weight to another eligible member, without exposing balances or linking identities. Delegated weight is aggregated privately at tally time.
await setDelegation({ data: { delegate } }); // delegate must be an eligible member
await clearDelegation(); // vote your own weight againThe server enforces two rules: you cannot delegate to yourself, and the target must already be an eligible member. Nullifiers continue to prevent double voting across delegation.
QRM Staking
QRM is a Token 2022 mint on devnet. Staking is a real on chain transfer from your wallet to the protocol vault, confirmed by the server before the ledger is credited. Tally nodes stake QRM to run the confidential counting network and earn per vote fees. Misbehavior, such as leaking a ballot, censoring, or a false tally, is slashed.
await qrmFaucet(); // devnet drip of QRM
const sig = await wallet.signAndSendTransaction(stakeTx); // Token 2022 transfer to the vault
await confirmStake({ data: { signature: sig } }); // server confirms on devnet, credits
await unstakeQrm({ data: { amount } }); // vault returns QRM, debits the ledgerBuffer, which browsers do not provide by default. QUORUM polyfills it before the SDK runs, so the wallet transfer builds correctly.Proofs and Verification
The Proofs surface publishes every closed vote, tally attestation, treasury solvency proof, and tally node record with one click verification. Verification requires no wallet and no trust in QUORUM, which is the point: an outsider can confirm the protocol was honest.
Two levels of verification exist. A record level check confirms a verified proof exists for a reference. A cryptographic check re runs the DLEQ math on the tally transcript itself.
On Chain Anchoring
A single Anchor program, quorum_anchor, is deployed to Solana devnet. It anchors the eligibility root and the tally result hash, and it embeds a real BN254 Groth16 verifier built on the alt_bn128 syscalls, which verifies solvency proofs on chain. The footprint is intentionally tiny: roots and hashes, never per ballot accounts, so rent stays minimal and vote accounts are closable.
| Anchored item | Meaning |
|---|---|
| eligibility_root | The Merkle root of the eligibility snapshot for a vote. |
| tally hash | A commitment to the final result and the correctness transcript. |
| solvency proof | A treasury range proof verified on chain by the program. |
Developer SDK
The @quorum/sdk package exposes the same client side primitives QUORUM uses in production, so anything you seal is compatible with the live tally. Everything runs client side: no server, no secret keys, no trust. You seal with public inputs, and you verify with only the published transcript.
npm add @quorum/sdk
import { sealBallot, verifyTally } from "@quorum/sdk";
// Seal a vote client side. Your choice never leaves the browser in the clear.
const ballot = await sealBallot(tallyPubkey, "yes", ["yes", "no", "abstain"]);
// Verify any published tally trustlessly. No server, no keys.
const { verified, totals } = verifyTally(transcript);| Export | Purpose |
|---|---|
| sealBallot | Encrypt a one hot vote to the tally key, returns ciphertext and commitment. |
| verifyTally | Trustlessly verify announced totals with DLEQ proofs. |
| sealProposal, verifyProposalReveal | Commit a hidden proposal and check a later reveal. |
| generateAuditorKey, encryptToAuditor, decryptAsAuditor | ECIES selective disclosure. |
The current release covers sealing and verification. Driving a deployment, such as creating votes or submitting ballots, uses authenticated server functions and is paired with your own transport.
Security Model
QUORUM is designed to fail closed. Each guarantee is enforced by cryptography, not by policy.
| Property | How it holds |
|---|---|
| Ballot privacy | Choices are encrypted client side to a threshold key; no single party can decrypt. |
| Receipt freeness | Ciphertexts are re randomized, so a voter cannot prove a choice to a briber. |
| No double voting | A unique nullifier per voter per vote, enforced in the database and at tally. |
| Tally correctness | Chaum Pedersen DLEQ proofs make the totals verifiable by anyone. |
| No live tally | The running result is hidden until close, which defeats whale following. |
| Solvency without exposure | A range proof proves reserves meet a threshold while the balance stays hidden. |
| Fails closed | An invalid proof is rejected; no valid proof means no execution. |