Mail-ballot chain of custody

It proves the envelope moved.
It can never prove how you voted.

Most of an American mail ballot's life is invisible. It leaves the election office, crosses the postal system twice, and comes back — and for most of that journey nobody can prove where it was. This is the work of closing that gap, with every stage's real status written down instead of glossed.

Status, in the same words as the meeting brief. Open-source, end-to-end-verifiable election software. Not yet EAC-certified under VVSG 2.0, and not a certified system of record for binding government elections. Any voters and candidates in a proof run are synthetic. Consult a cryptographer and election counsel before binding use.

In thirty seconds

What this does

A mail ballot travels a long way. Out to you, back to the office, into a locked room, then to the count. Today, much of that trip is a blank space.

This records every hand-off. Who had the envelope. When. Signed, in order, so a later change shows up.

What it will never do

It does not know how you voted. It cannot know. There is no place in it to put a vote.

It tracks the envelope, not the ballot inside. When the two are separated, the trail stops. That is the design, not a gap in it.

01 · The problem

A ballot spends most of its life where nobody is watching

Ask a county clerk where a specific mail ballot was last Tuesday and, in most jurisdictions, the honest answer is a shrug and a tracking number that belongs to the post office, not to the election.

That gap is not a scandal. It is a design gap. The ballot is tracked as mail — by a barcode meant for sorting machines — and it becomes an election record only when it lands back on a desk. In between, custody is a paper log, a locked cage, and people's memories.

When something does go wrong — a tray misrouted, a drop box emptied late, a batch that arrives without a signed transfer — the question that follows is always the same, and it is almost always unanswerable after the fact: who had this, and when?

What a voter actually wants to know

Did it get there? Was it counted? And can anyone tell how I voted? The first two deserve a real answer. The third must always be no — including from the people who run the system.

What a clerk actually needs

A record that survives a challenge. Not a dashboard — an evidence trail where every hand-off is signed, gaps are visible rather than smoothed over, and alteration after the fact is detectable by somebody who does not trust the office.

02 · What chain of custody means

An unbroken, signed answer to one question:
who held this, and when?

For a voter

Chain of custody is the reason you can be told your envelope arrived and was accepted without anybody, anywhere, being able to open the question of what was inside it. The tracking follows the package. Your vote separates from your identity before counting, and after that point the trail deliberately goes cold.

A confirmation is not a receipt. It tells you that a package was received. It never tells you — or anyone leaning over your shoulder — what it contained. That distinction is what makes vote-buying and coercion hard, and it is worth protecting even though it makes the product less impressive to demo.

For a county clerk

Chain of custody is what turns "we're confident it was handled properly" into something a court can read. Each transfer is recorded with two people, timestamped and signed. Nothing is edited: a correction is a new entry that points at the old one, so the original mistake stays visible.

The valuable property is not that the record is true — no system can promise that. It is that the record cannot be quietly changed. If someone alters an entry after the fact, the next verification fails and says exactly where.

The custody record must be reconstructable and its alteration detectable by someone who does not trust the operator. Threat model v1.0, §1 — what is being protected, in priority order

That threat model puts the franchise above its own bookkeeping. Where a broken custody record conflicts with a lawfully entitled person voting, the person wins: a human adjudicates, and the ballot is secured first. A system that let its own filing error disenfranchise somebody would have inverted its purpose.

03 · Front to back

Every stage, and what is actually built

Seven stages carry a mail ballot from an election office to an audit. Some of this is real, tested, running code. Some is written and never connected to anything. One stage is deliberately absent. Each is labelled below with what is true today, not what is planned.

STAGE 1

Issuance

Prototype

A voter is entitled to a ballot, and a package is created for them. The lifecycle token is minted here — the identifier the rest of the chain follows.

What is true today: token issuance runs, and you can drive it yourself in the demo below. It is not connected to any voter registration system, so nothing here decides who is entitled to a ballot. That decision stays with the jurisdiction, where it belongs.

STAGE 2

Digital tokens

Working — runs in your browser

Each package gets an opaque token with its own state machine: twelve states, nineteen event types, every transition checked before it is allowed. The token is what moves through the system — never a name, never a selection.

What is true today: this is the most complete part. The engine is 1,150 lines with no dependencies and no network, its Ed25519 is checked against the RFC 8032 test vectors, and its own suite is 24 of 24 passing, a second implementation is pinned to it byte for byte by SHA-256, and 43 conformance tests hold the two together. It runs entirely in the tab you have open. Nothing persists — close the page and the ledger is gone.

A limit its authors published themselves: the Merkle padding rule duplicates the final node on an odd count, so [A,B,C] and [A,B,C,C] produce the same root. A root alone does not pin the event count.

STAGE 3

Mail

Built — the feed is not connected

The package crosses the postal system twice. Each scan the post office makes is pulled in and bound to the token, so an outbound leg and a return leg become two parts of one record.

What is true today: the barcode serialisation and the tracking ingestion are written and tested — 9 tests covering the cases that matter, including that an unmapped code is never guessed at. The barcode symbol itself is not rendered, and the postal service identifiers are left deliberately blank rather than guessed. The live postal feed has never been connected — the credentials do not exist. This is named, in the project's own register, as the critical path, and it is the single largest gap on this page.

Why the two identifiers differ: the identifier printed on the envelope and the internal custody identifier are deliberately unrelated. If one could be derived from the other, anyone who photographed an envelope could compute the custody identifier and follow that voter.

STAGE 4

Ballots

Out of scope — on purpose

Marking, counting, adjudicating, tabulating: none of it happens here, and that is the point.

What is true today: there is no field anywhere in the custody system that could hold a vote. A build test walks every column and fails the build if one appears. That absence is not an omission to be filled in later — it is what keeps this outside the regulatory perimeter of a voting system, and what makes it deployable alongside whatever equipment a county already owns.

STAGE 5

Custody

Built — never deployed

Inside the election office: intake, transfers between staff, storage, and the two-person separation of the ballot from the envelope that ends the trail.

What is true today: the best-tested stage. A transfer refuses an empty second party and refuses to let a recipient witness their own receipt; gaps in the record are printed rather than bridged; a broken seal is raised as an incident and never written back over. Its suite is 128 tests passing, a 78-check smoke test over the real HTTP stack, and 54 of 54 deliberate faults caught. It runs on a developer's machine and has never been deployed anywhere. The signing key is an environment variable, not hardware.

STAGE 6

Verification

Working — and narrower than it sounds

Two different things share this word. One is a voter asking "did mine arrive?" The other is an outsider checking that the published record has not been edited.

What is true today: record verification works, and you can run it below — the chain is recomputed in your browser and reports exactly which entry broke. The voter-facing lookup is designed to refuse, and to say why: it confirms that a package was received, never what it contained, and a spent token reads identically to an accepted one so that timing cannot leak to a coercer.

What it is not: this is not end-to-end-verifiable voting. It does not verify cast-vote records or ballot receipts, and nothing here lets you check how a vote was counted. A separate research track does that; it is unaudited and is not part of this site's claims.

STAGE 7

Audit

Implemented — not runnable here

After the count, a sample of paper is checked against the reported result until the outcome is confirmed or a full hand count is triggered.

What is true today: two standard risk-limiting audit methods are implemented and tested in Python, including the cases that matter — a wrong outcome escalates to a full hand count, and missing paper is treated as the worst case. Its sample sizes match the published benchmarks. None of it runs in a browser, so there is no live audit demo on this page; claiming one would be easy and false.

The honest boundary: a ballot cast entirely on a device has no voter-verified paper record, so no risk-limiting audit can say anything about voter intent for it. That is not fixable with better cryptography.

04 · A demo you can break

Drive the lifecycle yourself, then tamper with it

This is the real engine, not a picture of one. Every step you press is hashed, chained to the step before it, and signed — here, in your browser. Nothing is sent anywhere, nothing is stored, and there is no invented election behind it: no voter, no name, no candidate, no vote. The last button deliberately corrupts a record so you can watch the verification catch it.

The package right now

State:

Events recorded: 0 · Not anchored yet.

Next:

Starting…

The signed event log

If the demo does not start, this browser is missing the Web Crypto APIs the engine needs — it will say so above rather than pretending to work.

05 · Threat model

Written to be read by someone trying to find what is wrong with it

A threat model that only lists defeated threats is marketing. This one names what the system does not defend against as carefully as what it does.

AdversaryWhat they are assumed to haveWhat they want
Outside attackerNetwork access to the public pagesEnumerate voters; learn who has voted; take the service down
Someone holding an envelopePhysical possession of one mailpiece and its printed codesTrack that voter; impersonate the return
A coercer or vote buyerOngoing proximity to a voter; may control their mailVerify compliance; punish deviation
A malicious insiderValid staff credentials in one roleAlter a record; suppress or fabricate a package
Two insiders working togetherTwo credentials, including a supervisorDefeat the two-person rule
A compromised hostFull database write accessRewrite history without it showing

The last row is the honest one. A privileged attacker holding every database, every signing key, every checkpoint and every backup could fabricate a replacement history. Hash chaining alone does not stop that. What makes it expensive is keeping signing keys out of the application database, rotating and auditing them, protecting the checkpoints, and retaining independent verification material — none of which this project has yet done in a real deployment.

Cryptography can demonstrate that recorded bytes have not been altered in defined ways. It cannot prove that an allegation contained inside those bytes is true. Published limits, shipped with the engine

06 · What this refuses to be

The list of things it does not do

Not a voting system

No column anywhere can hold a vote, and a test fails the build if one appears. It does not count, tabulate, adjudicate or report votes.

Not a receipt

Verification confirms that a package was received, never what it contained — so it cannot be used to prove a vote to a buyer or a bully.

Not certified, by anyone

No EAC certification, no VVSG conformance claim, no USPS approval, no NIST endorsement, no legal-compliance claim in any jurisdiction.

Not audited

No cryptographer review and no election-law review has been completed. Both are prerequisites, and both are outstanding.

Never piloted

No jurisdiction has run this, in any election, at any stakes. The next honest step is one low-stakes off-cycle election where nothing binding depends on it.

Not imposed from above

Elections here are run by states and their local jurisdictions. Nothing in this assumes or asks for a mandate. Adoption is voluntary and adopter-controlled.

A completion claim that cannot be checked is marketing. Everything above is written by name rather than summarised into a percentage, and the demo on this page is the part you can check yourself without taking anyone's word for it.