QUORUM
( QRM ) · Protocol Documentation

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.

Introduction

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 solvency is proven by range proof against the real treasury account, with selective disclosure to auditors.

The protocol is organized into modules, each backed by real cryptography rather than a placeholder:

ModuleWhat it provides
VotingSealed ballots, ZK eligibility, threshold tally, verifiable correctness.
ProposalsHidden until execution, commit reveal, on pass or timelock.
TreasuryZK solvency proofs bound to the treasury account, auditor disclosure. Confidential balances are planned.
MembersEligibility snapshot and private delegation of voting weight.
StakingReal Token 2022 QRM staking that settles on chain on devnet.
ProofsA public surface where anyone verifies the protocol was honest.
How it fits together

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 staking

A 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.

Source
Server functions live in src/fn. Client crypto lives in src/lib/crypto. Solana helpers live in src/lib/solana. The on chain program is under onchain/.
Sign In With Solana

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 session

Nonces 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.

Module I

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. The server sums the ciphertexts and recovers only the per option totals. On devnet all five shares are held by the operator's server, so the operator could decrypt an individual ballot; independent tally nodes holding one share each are planned.

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 stored sealed ballots under the published tally key, without trusting the server's arithmetic.

Receipt free
The protocol re randomizes ciphertexts so a voter cannot prove to a briber what they submitted. The participation receipt proves that you voted, never how you voted.
Server functionPurpose
castBallotVerify the ZK proof against the live root, store the nullifier and ciphertext.
runTallySum ciphertexts, threshold decrypt, write totals plus a DLEQ transcript.
verifyTallyRe verify the DLEQ transcript so totals are provably correct.
Module II

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 verified

Reveal 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.

Module III

Confidential Treasury

The treasury stays accountable without the app publishing its balance. Solvency is proven with a zero knowledge range proof over the real treasury account, and individual records can be disclosed to a chosen auditor without exposing the rest.

Solvency proofs. The operator picks a public threshold. The server reads the treasury account's balance from chain at a finalized slot and generates a Groth16 range proof that it is at least the threshold; nobody types the balance in. The app publishes the proof, the threshold, the account and the slot, but not the balance. The opening of the commitment is encrypted to every active auditor key, so an auditor can check it against the chain. The program verifies the proof on chain and accepts it only from the server's authority key. On devnet the treasury is a plain Token-2022 account, so its balance is still readable on chain; confidential balances are planned.

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 functionPurpose
recordSolvencyProofProve the treasury account's on-chain balance meets a threshold, seal the opening to auditors, verify on chain.
issueDisclosureEncrypt one record to an auditor public key (ECIES).
addAuditor, revokeAuditorManage the auditor keys authorized for disclosure.
Module IV

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 again

The 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.

Network

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. Staking at least 100 QRM makes a wallet eligible to vote; it pays no yield or fees. Planned: tally nodes stake QRM to hold key shares, with slashing for leaking a ballot, censoring, or a false tally. Neither tally nodes nor slashing exist on devnet yet.

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 ledger
Browser note
The Solana SDK expects the global Buffer, which browsers do not provide by default. QUORUM polyfills it before the SDK runs, so the wallet transfer builds correctly.
Public trust surface

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.

Settlement

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 itemMeaning
eligibility_rootThe Merkle root of the eligibility snapshot for a vote.
tally hashA commitment to the final result and the correctness transcript.
solvency proofA treasury range proof verified on chain by the program.
For integrators

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 and no secret keys. 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);
ExportPurpose
sealBallotEncrypt a one hot vote to the tally key, returns ciphertext and commitment.
verifyTallyTrustlessly verify announced totals with DLEQ proofs.
sealProposal, verifyProposalRevealCommit a hidden proposal and check a later reveal.
generateAuditorKey, encryptToAuditor, decryptAsAuditorECIES 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.

Guarantees

Security Model

QUORUM is designed to fail closed. The table shows what holds on devnet today, including where it still relies on the operator.

PropertyHow it holds
Ballot privacyChoices are encrypted client side to a 3 of 5 threshold key. On devnet one server holds all shares, so the operator is trusted not to decrypt single ballots.
Receipt freenessCiphertexts are re randomized, so a voter cannot prove a choice to a briber.
No double votingA unique nullifier per voter per vote, enforced in the database and at tally.
Tally correctnessChaum Pedersen DLEQ proofs make the totals verifiable by anyone.
No live tallyThe running result is hidden until close, which defeats whale following.
Solvency without exposureA range proof shows the treasury account's on-chain balance meets a threshold without the app publishing it. On devnet the balance is still readable on chain.
Fails closedAn invalid proof is rejected; no valid proof means no execution.
Status
QUORUM runs on Solana devnet. A mainnet promotion is gated behind third party audits. Review audits before relying on the protocol with real value.
Open the Dashboard