Security & Trust

Ballot Anonymity and Voter Receipts: How VoteAlly Protects Both

Every election faces the same tension: voters want proof their ballot was counted, but the vote itself must remain secret. VoteAlly solves this with cryptographic receipt codes that verify inclusion in the final tally without ever revealing who voted for whom.

Published: March 2026

Ballot anonymity with voter receipts means that each voter receives a unique 12-character cryptographic code after submitting their ballot. This code can be checked against a published list of all receipt codes to confirm the ballot was counted. The receipt reveals nothing about the vote choice or the voter's identity. VoteAlly enforces anonymity structurally: the Ballot table has no voter ID column, vote choices are encrypted with AES-256-GCM, and timestamps are rounded to the nearest hour to prevent correlation.

The anonymity-auditability tension

Paper ballots handle anonymity well: you mark a ballot, fold it, and drop it in a box. No one can trace it back to you. But paper ballots are terrible at auditability. After the box is opened and votes are counted, you have no way to confirm that your specific ballot made it into the final tally. You have to trust the process.

Digital voting platforms have the same fundamental challenge. If you give every voter a way to look up their ballot, you risk creating a link between the voter and their choice. If you keep ballots completely anonymous, voters have no way to verify the count includes their vote.

VoteAlly addresses both sides of this problem. Anonymity is enforced at the database structure level, not just the application logic level. And auditability is provided through cryptographic receipt codes that prove a ballot exists without revealing anything about it.

How ballot anonymity works

VoteAlly's anonymity is not a policy or an access control rule. It is a structural property of the database. Four layers work together to make it impossible to trace a vote back to a voter, even with full database access.

No voter-to-ballot link in the database

The Ballot table has no voter ID column. There is no foreign key, no reference field, and no way to join a ballot back to a voter. Participation is tracked separately: the VoterParticipation table records that a voter voted on a question, but never what they voted for.

AES-256-GCM encryption of ballot choices

The actual vote selection is encrypted with AES-256-GCM before it reaches the database. The plaintext selection field is stored as NULL. Only the encrypted payload and HMAC tally hashes are kept. Even with full database access, the vote content is unreadable without the encryption key.

Hourly timestamp bucketing

Ballot timestamps are rounded down to the top of the current hour. If you vote at 2:35 PM, your ballot is timestamped 2:00 PM. This prevents "time correlation attacks" where someone could match a voter participation timestamp to a ballot creation time.

HMAC-based receipt codes

Each ballot gets a 12-character receipt code generated using HMAC-SHA256 with a server-side secret. The code is shown to the voter immediately after submission. It proves the ballot exists without revealing the vote or the voter.

These protections apply to every ballot, regardless of session type. For more on the infrastructure behind these safeguards, see the security overview.

How receipt codes work

When a voter submits their ballot, VoteAlly generates a receipt code and returns it immediately. Here is what happens, step by step:

1

Ballot is created with a unique ID

Every ballot gets a UUID (universally unique identifier) generated on the server before it is saved. This ID is internal and never shown to the voter directly.

2

Receipt code is derived from the ballot ID

The server computes HMAC-SHA256 using the ballot ID and a secret key (AUTH_SECRET), then takes the first 12 characters of the result in uppercase. This produces a short, unique, and unforgeable code like "A7F3B2C91D04".

3

Ballot is saved with encrypted choices

The vote selection is encrypted using AES-256-GCM and stored alongside HMAC tally hashes. The plaintext selection is never saved. The receipt code is stored on the ballot record and enforced as unique by a database constraint.

4

Receipt code is returned to the voter

The voter sees their receipt code on the confirmation screen. This is the only time the system connects the voter to this specific code. Once the voter leaves the page, the connection exists only in the voter's memory or notes.

5

Voter verifies after the session

After voting ends, the administrator can export or publish the full list of receipt codes. The voter checks that their code appears in the list. If it does, their ballot is confirmed as part of the final tally.

How votes are tallied without decrypting ballots

A common question is: if votes are encrypted, how does VoteAlly count them? The answer is HMAC-based tally hashes.

When a ballot is submitted, each selected candidate ID is hashed using a derived secret key. The database stores these hashes alongside the encrypted ballot. To tally votes, the system computes the same hash for each known candidate and then groups ballots by matching hashes in the database.

The database engine only ever sees opaque strings. It never processes candidate names or plaintext vote choices. This means tallying is fast (a standard SQL GROUP BY query) and secure (the database never has access to what the hashes represent without the application secret).

How this compares to paper ballot verification

Paper ballots

  • Anonymous once dropped in the box
  • No way for a voter to verify their specific ballot was counted
  • Relies on scrutineers and observers for trust
  • Susceptible to counting errors and ballot loss
  • No audit trail linking a voter to proof of inclusion

VoteAlly cryptographic receipts

  • Anonymous by database structure (no voter ID on ballots)
  • Every voter gets a unique receipt code to verify inclusion
  • HMAC-based codes are unforgeable without the server secret
  • Encrypted ballots survive indefinitely for audit
  • Receipt list can be published for public verification

Scenario: a condo board member challenges the results

Worked example

A 150-unit condo association in Florida runs its annual board election through VoteAlly. The results show Candidate A winning by 8 votes. A homeowner who supported Candidate B sends an email to the board president: "How do I know my vote was actually counted? I don't trust digital voting."

The board president exports the list of all receipt codes from VoteAlly's reports page and publishes it in the next board communication. The list contains 142 receipt codes (one per ballot cast), with no names or vote choices attached.

The concerned homeowner checks the list and finds their receipt code, "B9E4F1A72C03", which they saved after voting. Their ballot is confirmed in the count. They still cannot see anyone else's vote, and no one else can determine how they voted from the receipt list.

  • The homeowner verified their ballot without contacting the administrator
  • The board demonstrated transparency without compromising ballot secrecy
  • The published receipt list contains no voter names, emails, or vote choices
  • The same verification works for every voter in the session
  • No special technical knowledge is needed to check a receipt code

This is the kind of post-election trust that paper ballots simply cannot provide. The voter gets individual verification. The board gets institutional credibility. And anonymity is never compromised.

Frequently asked questions

How does VoteAlly keep my vote anonymous?

The Ballot table has no voter ID column. There is no foreign key, no reference, and no way to join a ballot record back to a voter record. Voter participation is tracked in a separate table that records who voted on which question, but never what they voted for. Ballot timestamps are also rounded to the nearest hour to prevent time-based correlation.

What is a voter receipt code?

A 12-character uppercase string generated using HMAC-SHA256 from the ballot ID and a server-side secret. It is shown to you immediately after you submit your ballot. You can use it to verify your ballot appears in the final tally without revealing your identity or your vote choice.

Can an administrator see who voted for whom?

No. Ballot choices are encrypted using AES-256-GCM before storage. The plaintext selection is never saved. The Ballot table contains no voter reference. Even with full database access, there is no way to link a specific ballot to a specific voter.

Can someone forge a receipt code?

No. Receipt codes are generated using HMAC-SHA256 with a secret key that only exists on the server. Without this key, producing a valid code is computationally infeasible. Each code is also enforced as unique at the database level.

How do voters verify their vote was counted?

After a session ends, the administrator can publish or export the full list of receipt codes. Each voter checks that their personal receipt code appears in the list. If it does, their ballot is confirmed as part of the count. The code reveals nothing about the vote choice or the voter identity.

What happens to receipt codes after PII is purged?

Receipt codes survive the PII purge process. When voter personal information is removed (by default, 90 days after a session ends), the anonymous ballots and their receipt codes remain intact. The audit trail stays verifiable indefinitely.

Related guides

Run elections your members can verify

VoteAlly is free for up to 50 voters. Every ballot gets a cryptographic receipt, and every vote is anonymous by design. No credit card required.