Skip to main content
VotersRight

Methodology

How VotersRight works — and how to check it

We would rather you verify us than trust us. This page explains exactly how the platform works, names every data source, and shows you how to confirm any number against the public Solana ledger yourself. Where something isn't finished, we say so.

Last updated May 2026

Section 01

How We Verify Identity

Every vote on VotersRight comes from a verified U.S. resident — not an anonymous click. To cast a vote you complete a three-step check: a one-time passcode sent to a U.S. mobile number, a residential address resolved to a Congressional district, and a Solana wallet provisioned to your account so your position can be recorded on a public ledger.

One phone number maps to one address; changing your address requires re-verification. We store a salted hash of your phone number, never the raw number, and we never sell or share it. What we can't promise is perfection: a determined individual could obtain multiple numbers. Section 06 describes how we detect the coordinated burst patterns that abuse at scale would produce.

Sources & verification

Section 02

How We Count Votes

A heartbeat is the live tally of verified constituents who have taken a Yes/No position on their senator, alongside the reason they cite. Each position is written to a Solana smart contract as an on-chain record — not a private number in our database that you have to take on faith. The heartbeat you see is a cached aggregate of those on-chain records, refreshed within seconds.

These constituent positions are distinct from a senator's official roll-call votes in Congress. Those come from Congress.gov (Section 03) and appear separately on each senator's page. You can verify any heartbeat count yourself against the chain — see Section 08.

Sources & verification

Section 03

How We Source Data

Everything we publish about a senator traces to a named public source, linked inline on their page. We ingest on a schedule, timestamp every capture, and cite the original record on every figure. A machine-readable sample export of what we publish — no account required — is available below, in JSON or CSV.

Download a sample export

Inspect exactly what we publish before you cite us — no account, no signup. The export carries senator metadata and the aggregate constituent heartbeat (vote counts only). It contains no user, voter, address, or wallet data of any kind. Cached at the edge for one hour.

Schema
Per senator: bioguide_id, full_name, party, state_code, term dates, official website, lifecycle state, and the heartbeat aggregate (heartbeat_yes_count, heartbeat_no_count, heartbeat_total_count, and the yes/no percentages). The JSON body adds a top-level weekly_digests list and a generated_at timestamp.
Try it (FR52)
# JSON
curl https://v2.votersright.org/api/v1/exports/sample.json

# CSV
curl https://v2.votersright.org/api/v1/exports/sample.csv

The export's weekly_digests list is intentionally empty for now — the weekly-digest summaries populate it once the weekly-digest pipeline ships. We do not fabricate digest rows in the meantime.

Section 04

How We Handle Corrections

We treat errors as a public ratchet, not a private embarrassment. Anyone can report an error on any data point inline — no account required — and we triage within 24 hours. When we correct something, the original wording, the corrected wording, the date, and the reasoning are published permanently to the Public Correction Archive (Section 07). Politicians' offices have a separate, identity-verified correction channel.

Section 05

Our Funding & Independence

VotersRight is independent. We are not affiliated with, funded by, or operated on behalf of any political party, candidate, campaign, or political action committee. We use no red/blue partisan framing anywhere on the platform — a senator's party appears only as a neutral letter label.

A full funding disclosure — who pays for VotersRight, and how — will be published here before public launch.

Section 06

Adversarial Test Results

Trust requires proof that we tried to break ourselves. Before public launch we will run adversarial "sybil" tests that simulate burner-phone farms and coordinated voting bursts, then publish the results — pass or fail, including methodology and detection thresholds — so you can judge the platform's detection posture rather than take our word for it.

See the published integrity-test results. If we ever detect a real coordination attempt in production, we commit to publishing it as a transparency report (Section 09).

Section 07

Public Correction Archive

Every correction we have ever issued is public and permanent — sorted most-recent first, each showing the original wording, the corrected wording, the date, and the reasoning for the change. Nothing is quietly edited away.

Section 08

Smart Contract Address & Audit Report

The voting smart contract is the source of truth for every heartbeat. It is currently deployed to the Solana devnet cluster for testing. The audited mainnet program and the independent security-audit report are in progress; both the mainnet program ID and the published audit will appear here before public launch.

Program & verification

Program ID (devnet)
5zskwM6GtRXmhPMHjM5rzAwpJ5DejZN4n8zY6F6ebk2b
Query it yourself (FR54)
# Solana CLI
solana account 5zskwM6GtRXmhPMHjM5rzAwpJ5DejZN4n8zY6F6ebk2b --url https://api.devnet.solana.com

# JavaScript (@solana/web3.js)
import { Connection, PublicKey } from '@solana/web3.js';
const conn = new Connection('https://api.devnet.solana.com');
const info = await conn.getAccountInfo(new PublicKey('5zskwM6GtRXmhPMHjM5rzAwpJ5DejZN4n8zY6F6ebk2b'));
console.log(info);
Per-senator heartbeat verification
Every senator's heartbeat is verifiable on chain: copy a runnable CLI command or a @solana/web3.js snippet that derives their on-chain Poll account yourself.
Key rotation & operational security
Key-rotation & operational-security runbook →

Verify it yourself

Verify a Heartbeat on Chain →

Pick any senator and confirm their heartbeat against the Solana ledger — a copy-to-clipboard CLI command and a @solana/web3.js snippet that derives the on-chain account from first principles.

Section 09

Off-Platform Limitations

Honesty about what we cannot do builds more trust than overstating what we can. VotersRight cannot detect off-platform vote-buying, coercion, or offline organizing campaigns — activity that never touches our system is invisible to us. We verify that a vote comes from a real, district-resolved U.S. resident (Section 01); we cannot see what happened to that person before they arrived, who paid them, or who pressured them off-platform. We name that limit plainly rather than imply a completeness we don't have.

What aggregate-trend analysis would surface is a coordinated pattern: a sudden statewide flip with no corresponding news event, for instance, stands out against a normal trend line. To be clear about what this is and isn't — it is a detection posture, an approach we would apply, not a live anomaly-detection system we are claiming to run against your data today. We describe the approach so you can hold us to it, not so you'll assume it is already deployed.

If we ever detect a real coordination attempt in production, we commit to publishing it as a transparency report — what we saw, how we saw it, and what we did — rather than quietly handling it. The same commitment runs the other way: we will publish the adversarial "sybil" test results that exercise this posture before we claim it works (Section 06).

Detection posture & evidence

  • Aggregate-trend analysis is described above as an approach, not a deployed capability. We do not claim to run live anomaly detection on your data today.
  • The published adversarial sybil-test results — evidence that this posture actually surfaces coordinated bursts — appear on the dedicated integrity-tests page, cross-referenced from Section 06. They are forthcoming, published before public launch; we link nothing we have not yet run.

Section 10

Signed Broadcasts

Every weekly digest we publish is cryptographically signed with an Ed25519 key the moment it goes live. The signature covers the digest's exact content — its headline, summary, body, and the ranked movers — so anyone can confirm a number or a quote attributed to VotersRight actually came from us, unaltered. If someone doctors a screenshot of a digest and changes a figure, the signature no longer matches the content, and the forgery is detectable by anyone, not just us.

Verification needs nothing private. The signature and the public key travel with the published digest; you reconstruct the signed message from the public fields, then check the signature against the public key below. We never expose the private signing seed — only its public half is published here.

Authorized signing keys

  • Key ID
    vrp-digest-2026a
    Public key (Ed25519, base64)
    15Ptzwev6amKp0BIfrv4KBU5YkjSX1TiuDKIGsJMnak=
    Valid from
    2026-06-09T20:02:32.214Z
    Valid until
    Current (open-ended)

The signed message format (v1)

The signature covers the UTF-8 bytes of a deterministic, canonical string you rebuild from the digest's public fields. Line 1 is a fixed domain-and-version prefix; line 2 is a single-line JSON object whose keys appear in this exact order, with explicit null for absent optional values and the movers sorted by ranking_metric ascending, then rank ascending:

vrp-digest-signature/v1
{"slug":<string>,"week_start":<YYYY-MM-DD>,"week_end":<YYYY-MM-DD>,"had_movement":<bool>,"headline":<string>,"summary":<string>,"body_md":<string>,"movers":[{"bioguide_id":<string>,"ranking_metric":<string>,"rank":<int>,"total_delta":<number>,"net_supporter_delta":<number>,"yes_pct_delta":<number|null>,"top_reason_code":<string|null>,"top_reason_count":<int>}]}

Verify it yourself (Node.js, no dependencies beyond @noble/curves):

import { ed25519 } from '@noble/curves/ed25519.js';

// 'sig' = published digest.signature (the self-describing blob).
// 'message' = the canonical v1 string above, rebuilt from the public digest.
const message = 'vrp-digest-signature/v1\n' + canonicalJsonBody; // see format above
const pub = Buffer.from(sig.public_key, 'base64');
const signature = Buffer.from(sig.signature, 'base64');
const ok = ed25519.verify(signature, new TextEncoder().encode(message), pub);
console.log('authentic:', ok); // true ⇒ the digest is unaltered VotersRight data

// Trust the key: confirm sig.public_key matches an authorized key above and
// that sig.signed_at falls within that key's valid_from / valid_until window.

Key rotation

We rotate the signing key annually. When a new key is published, the previous key stays authorized for a 30-day overlap window (its valid_until is set to the new key's valid_from plus 30 days). During that window, signatures from either key verify — so a digest signed just before a rotation stays checkable while the new key takes over. A signature is only ever trusted if its key was authorized at the moment it was signed.