CreateConnectProtect
The best end-to-end cryptographic process for a mail-ballot election, assembled from the work of Krissy Hall across 58 repositories built June 6 to August 27, 2026.
Mail-ballot election · end to end
Two systems that share exactly one bit and no join key, plus paper that carries the vote, plus a published record a stranger can recompute.
Verify hard, then sever.
13 stages 19 signed event types 12 custody states 3-of-5 guardian quorum 670 day retention floor
Executive summary · one page, sixty seconds
This is a way to run a mail-ballot election so that anyone can check the result, and nobody can find out how you voted. It is built, and it is measured. Most of it can be adopted this year, because most of it never touches the voting machines.
One. The system that proves who you are and the system that tracks your ballot package are kept apart, and they share exactly one small fact and no shared reference number, so nothing in either one can reconnect a person to a vote.
Two. The paper ballot is still the thing that counts, and an open mathematical count runs beside the paper as a check on it, never instead of it.
Three. Everything the county does goes into a record it can add to but never quietly change, and a fingerprint of that record is published while the outcome is still unknown, so a stranger with a laptop can recompute the totals and catch a later edit.
Election officials who need evidence they can hand to a skeptic. Legislators and funders who need to know what is real. Counsel who needs to know which parts sit inside the certification boundary and which do not. You do not need the mathematics to judge the design. Every section on this site opens in plain words, with the technical depth underneath it.
The evidence layer, called Track A here: custody tracking, tamper-evident logs, published signed fingerprints witnessed by outsiders, a checker anyone can download and run offline, a privacy test suite, support for a paper risk-limiting audit, and an honest report after the election. None of it marks, reads, or counts a ballot, which is why a county can adopt it without waiting on federal certification.
The counting path, Track B, follows the certification clock into the 2027 primaries and the 2028 cycle, because federal certification of a voting system takes six months to more than a year. That split is the whole schedule.
Start anywhere: the answer in one page, the thirteen stages, the road to November 3, or the glossary.
Every figure is sourced in section 05. Every external date is linked to its governing authority in section 08.
Section 01
One page that carries the whole design. Two systems kept apart, paper that carries the vote, and a public record anyone can check for themselves.
Read only this and you have the whole design. Everything after it is detail, evidence, and dates.
A mail-ballot election runs as two systems that share exactly one bit and no join key, plus paper that carries the vote, plus a published record a stranger can recompute.
It proves the person as hard as the law allows. Registration and eligibility live here. Verification is per person, with notice and a cure path, and never by bulk matching against death, felony or citizenship lists. Anyone who cannot travel clears verification by remote online notarization. The sorting rule that decides who is automatically enrolled and who is asked to verify is published, fixed before the election, and logged per decision, so demographic skew is detectable. Nobody is silently removed.
The identity side emits one object into the ballot side:
{election_id, jurisdiction, ballot_style, eligible}. A fifth field is
refused at construction. No name, no registration reference, no precinct, no
timestamp. Then the voter's own device generates a random credential value, blinds it, the registrar signs the blinded value, and the device unblinds it. The registrar
can prove it issued exactly one credential per eligible voter and cannot connect any
credential to any voter, because it only ever saw random noise.
There is no token vault, so there is nothing to steal.
Tokenize identifiers, never human beings. Design rule 1 of 2, THE PROCESS, preamble
It tracks the physical mail-ballot package, and only the package. A 256-bit random token, raw_token, is handed once to the controlled print step and never stored;
only sha256(raw_token) persists. A separate internal
token_id is a ULID that never appears on an envelope. A third
identifier, the USPS Intelligent Mail barcode serial, is public because postal
equipment scans it - and no function derives any one of the three from any other,
in either direction. The custody record moves through a twelve-state machine on
nineteen signed, hash-chained event types. Postal scans are advisory transport
evidence and never an acceptance decision, and never a prerequisite for counting a
lawfully received ballot.
A ballot is accepted by an authorized human validator. Then two distinct officers
attest PRIVACY_SEPARATION_COMPLETED, the envelope is opened, the ballot
is separated, and the token goes SPENT. The ballot is written into a
sealed batch of at least k ballots, physically shuffled, and released to tabulation
carrying nothing that points back. After that moment the link is gone, not
hidden - there is no encrypted pointer, no key, nothing to decrypt. Custody stops
here. It never learns a selection. Tabulation never receives a token, an envelope
identifier or a postal reference.
Paper is authoritative. The cryptographic count runs alongside it, not instead of it: exponential ElGamal ciphertexts on a public bulletin board, each carrying a proof that it encrypts a valid selection, summed homomorphically, and decrypted by a 3-of-5 guardian quorum under threshold decryption drawn from adversarial parties, each publishing a Chaum-Pedersen proof of honest partial decryption bound to a commitment published at the key ceremony. Only the total is ever decrypted. The result is then checked against paper by a risk-limiting audit that escalates to a full hand count when the margin demands it.
Every custody event and every ballot is a leaf in a hash-chained, append-only log whose leaves form a Merkle tree. Tree heads - never bare roots, because a head commits the event count - are signed and published to at least three independent
custodians at phase boundaries: roll close, polls open, polls close, certification.
A hash published at poll close existed publicly before anyone knew the outcome.
Corrections are appended and carry corrects_event_id; there is no
update, edit or delete method anywhere. Retention runs 670 days minimum because
52 U.S.C. 20701 requires 22 months, a legal hold outranks any expiry, destruction
touches only terminal, unheld, past-retention records, tree heads are never
destroyed, and each destruction run emits a signed record that lists identifiers
and no content.
The record must be tamper-evident, never immutable. Design rule 2 of 2. Evidence of change, not a system nobody can correct.
Four fields: status, a plain-language label, a next action where one lawfully
exists, and a last-updated time. Nothing downloadable, no shareable URL, no event
history, no chronology precise enough to time a demand against, and
SPENT reads byte-identically to ACCEPTED. Authentication is
on something only the voter holds, rate limited, with no retained record of who
looked. No receipt proves how anyone voted, and re-voting supersedes so that
nobody, including a coercer, can tell which ballot was final.
A downloadable offline verifier built from the same crypto module the live system runs, plus the published bundle. Unplug the network and it re-derives every credential signature, the Merkle root from the raw leaves, every inclusion proof, every consistency proof against an earlier tree head, every guardian proof, and the whole tally. Every published figure is recomputed from the raw record, never echoed from a stored status flag.
The franchise outranks the bookkeeping. Where a broken custody record conflicts with a lawfully entitled person voting, the person wins: the ballot is secured first and a human adjudicates. Software does not decide the hard cases.
Source: THE-PROCESS.md section 1, "The answer in one page."
Section 02
This is the map. On one side, the part that proves who you are. On the other, the part that follows your ballot through the mail. Between them a wall that exactly one small fact crosses.
The identity side, the wall and the single object that crosses it, the custody side with its three non-derivable identifiers, the seam, the count, and the published record. Select any plate to read what it is, where it comes from, and what it guarantees.
Plates are selectable with a pointer or with the keyboard. The dashed line is the route one package travels: enrollment, the wall, custody, the seam, the sealed batch, tabulation, and the record.
Verify hard, then sever. kristenslab/verifiable-elections public/front-end-verification.html section 06
Section 03
Here is one mail ballot from start to finish, in thirteen steps. Each step says what happens in ordinary words, and what stops that step from being cheated. Then the technical detail sits underneath.
One mail ballot, from the roll to destruction. Each stage states what happens, the mechanism, what a voter can verify, what an auditor can verify without trusting the county, and what is trusted by procedure rather than proven by math. That last field is the mark of an honest design, and it is stated in full.
Every stage is deep-linkable: the address bar carries
#stage/registration through #stage/retention. Arrow keys,
Home and End move along the rail. Source: THE-PROCESS.md section 3.
Section 04
This is the moment that decides everything. Two officers open your envelope together and take your name off your ballot for good. After it, no key exists anywhere that could put the two back together.
Envelope separation is the single moment where a person is holding both halves at once. Everything the design is for is decided here.
A system that could join the two halves would have to hold the join key, and a held key is a key somebody can be compelled to use. After separation the link is gone, not hidden. A system where the link is encrypted is a system where somebody holds a key; here there is nothing to decrypt. That is a stronger property than any access-control regime, and it is the only version of the property that survives a subpoena, a rogue administrator, a database export and a redesign.
The custody record must be voter-identifying, so it must never be vote-touching.
You cannot mail a ballot to a hash.
bb2g-custody/custody/models.py binds
CustodyToken.registration_pk to a row holding a name and an address,
by necessity. The only safe response to a necessarily identifying database is to
make it structurally incapable of holding a selection, which is what
tests/test_boundary.py enforces by walking every mapped column, and
what schemas/event.schema.json enforces with
additionalProperties: false, rejecting rather than dropping, because
a silently dropped field looks identical to an accepted one.
Three data domains, separated by construction: registration may hold identity,
never choices; lifecycle may hold token, postal association and custody status,
never choices, never cast-vote records, never ballot images; tabulation may hold
anonymous ballot and batch data, never a voter identity, postal reference,
lifecycle token or envelope id. TABULATOR is a role that may write
zero event types.
BALLOT_ACCEPTED by an
authorized validation authority, a person and not a rule engine. Nothing has
been opened.PRIVACY_SEPARATION_COMPLETED,
attested by two distinct officers. The engine counts
new Set(officers.filter(Boolean)).size < 2 so array length cannot
fake it, and in the persisted service the same rule is a table CHECK constraint
so a direct SQL write cannot bypass the workflow.TOKEN_SPENT, refused without
a prior token.separation_completed. After it, a second acceptance
under that token is impossible and SPENT is terminal. In the
reference implementation PRIVACY_SEPARATION is the only transition
from ACCEPTED to SPENT, and asOf queries
do not follow it, so history cannot be replayed across the boundary
either.BALLOT_BATCH_SEALED. The
batch is not released until it holds at least k ballots, with k of 25 to 50
normal practice for mail batches, physically shuffled at the seal step and
attested by the same two officers, recorded with the batch id, the ballot count
and the seal serial number.token_id, no IMb serial, no postal
reference, no registration key, no per-ballot timestamp.Without step 4 the cryptography holds and the anonymity leaks, by index and by clock. Three concrete attacks follow, and the batch seal is where each is closed.
TOKEN_SPENT events carry a monotonic global_sequence.
Separation happens in a physical order. If the ballots enter the scanner in that
order, the n-th cast-vote record corresponds to the n-th TOKEN_SPENT.
No cryptography is broken. The join key is the index.
The same attack with clocks. PRIVACY_SEPARATION_COMPLETED and
TOKEN_SPENT carry millisecond recorded_time, and
tabulator scan logs are timestamped too. Over a low-volume hour, correlating the
two series reconstructs the pairing even if the batch order was partly
disturbed.
Registration carries precinct and
ballot_style. If a precinct returns two or three mail ballots and
per-precinct mail-only results are published, the anonymity set is two or three people. Custody decides which ballots land in which physical batch, so custody is
where this is fixed.
The answer was already written three days later in a different domain.
counted/docs/SPEC-TOKENIZATION.md 4.3: "Any monotonic field on both sides
of a wall links records with no key and no query - timestamps, sequence numbers, row
ids, file mtimes. No wall-clock time finer than the day may appear on either side, and
nothing may be published in arrival order." And 4.4: "Suppress any overlap below k
outright; do not describe it carefully." COUNTED enforces both, with 83 of
83 tests passing, including nothing-leaves.test.mjs, which runs a
two-hundred-contribution round and greps every byte the store wrote for every
respondent's name, date of birth and identifier. That is how you test a privacy
claim.
Two identifiers are not two people. The spec says so and
exports DUAL_CONTROL_LIMITATION, and the tests use
['a','b'] to make the point concrete. Serial-numbered tamper-evident
seals logged in and out, a supervisor countersignature, and camera coverage of the
separation table are what carry that weight.
A colluding officer pair is item 4.1 on
bb2g-custody's published list of things the system does not defend. The
answer is staffing rotation, drawing the pair from different reporting lines, and the
physical seal record.
The pairing rule.
custody-layer/src/lib/receipt.mjs SOFTWARE_INDEPENDENCE:
pair every digital custody event with a physical artifact, a signed transfer sheet, a
sealed and numbered container, a batch header initialed at intake. "The pairing is
what makes the log corroborating rather than constitutive, and it is also the
mitigation for HAVA 301 prong (D). One design decision, two problems solved."
Source: THE-PROCESS.md section 5.
Section 05
Nothing below is an estimate. Every number came from running her code during the audit and counting the result.
Every figure below was produced by running her code during the audit, with the count given.
verified-vote: 255 passed, 2 skipped, 257 collected, the two skips
guarding on ElectionGuard not being installed. tests/test_attacks.py is
31 passed, one per claim family C1 to C8.kristenslab/verified-voteelectionguard from PyPI, verified: a 5-guardian ceremony, 6 ballots
encrypted and validated by the SDK, tallied with 3 of 5 guardians present, reporting
the totals that went in.tools/verify_electionguard.pybb2g-custody: 128 tests passed and all 54 poisons caught, with the note
"Every check has now been seen to fail."custody-layer gates/run-all.mjsassertPhase(n, ctx) reads the log, verifies the whole chain first, and
throws unless every earlier phase carries a phase.attested entry;
attestPhase() refuses unless result.passed === true and
writes hashOf(result) into the log, so a later claim of "we ran it" is
checkable against a re-run. There is no environment variable or admin command that
opens a phase early, and a test greps the source tree to prove no override exists.
kristenslab/strongroom src/core/gate.mjs
A guardian proof verifying is not enough: each guardian's A1 must
equal the commitment they published at setup, or a guardian could submit a
consistent proof for a share that is not theirs and silently skew the result. Most
implementations of threshold decryption omit this one.
cast-verify lib/crypto/elgamal.ts; safe-elections-isolation engine/tally.mjs
No update, edit or delete method on the
ledger, and a gate asserts those methods do not exist. A correction is a new event
carrying corrects_event_id, walkable by
correctionChain(), and the original stays byte-identical. A doctrine
gate fails the build if the word "immutable" reaches a user-facing string.
kristenslab/election-command assets/lockchain.mjs
counted/test/vaultless.test.mjs assembles a genuinely valid
break-glass quorum - seven enrolled holders, four valid Ed25519 signatures from
four distinct organizations, real Shamir shares from a real ceremony - proves the
quorum is met, and then requires the door to come back with nothing, because no
identity was ever sealed. 83 of 83 tests pass in 3.2 seconds.
StrongRoomSE/counted test/vaultless.test.mjs
It transpiles the same crypto module the live app runs, embeds it verbatim beside
a frozen public bundle and a driver, and emits one .mjs. Network
unplugged, no install: it re-derives every credential signature, the Merkle root
from the raw leaves, every guardian proof and the whole tally, and exits non-zero
with "Do not trust this election's results as reported."
StrongRoomSE/safe-elections-org server/offlineVerifier.ts
The service throws at startup if no HSM-backed client is configured in production. Not a warning: a silent fallback to in-process keys would turn every deployment that missed a config value into a deployment with no tamper evidence, and that failure mode is invisible until something goes wrong. The same shape enforces the 670-day retention floor.
operation-encrypt-ballot-lifecycle spec section 10.4
Every failed write appends a signed, chained record: replay-flavored failures
become DUPLICATE_OR_REPLAY_DETECTED, everything else
EVENT_REJECTED. In the persisted service the failing append sits
inside a transaction about to roll back, so the rejection row is written in its own
committed session, because otherwise the system silently forgets every attempt it
refused.
ballot-trail assets/mblts.mjs; bb2g-custody custody/ledger.py
Spec section 18.3 specifies the exact warning text logged at startup and on every
distribution run about custodian independence being a governance arrangement, and
records declaredOperator and declaredJurisdiction, so if
an auditor later finds two declared custodians share an operator, that fact is
visible in the record alongside the tree heads they witnessed. 18.4 does the same
for dual control.
operation-encrypt-ballot-lifecycle spec 18.3 and 18.4
D5: a completion claim that cannot be checked is marketing, and the model to copy is the completion matrix that reports zero complete rows rather than a flattering average - 748 cells, zero complete rows. Module invariant M04 refuses to emit a readiness score at all if any component cannot be measured. M49: "a backup nobody has restored is a belief."
dexters-lab docs/decisions.md D5; election-command matrix.html
Source: THE-PROCESS.md section 6, items 1 to 30.
Section 06
Every part of this design already exists somewhere in her work. This table names the repository and the file that provides each part, and says plainly whether it is running code or a written specification.
One hundred and eight components, each with the repository and path that provides it. Filter by status or by stage, or search a path.
| Stage | Component | Repo and path | Status |
|---|
Source: THE-PROCESS.md section 4, rows reproduced in full.
Section 07
Ten things to close before a real election, in order, each with the specific repair and an honest size. None of them needs a new invention.
Ten items to close before a real election, in priority order, each with its specific repair and an honest size. Then the build order: fourteen steps, each sized to finish. Nothing here requires a new invention. Every item is either wiring two of her own repositories together or writing down a procedure the code already assumes.
Where. verified-vote/src/blindfold/token/ledger.py, with the
same shape in cast-verify/db/001_schema.sql.
The repair. Append SUPERSESSION_RECORDED as its own chained,
signed event carrying the superseded event's sequence and digest, include it in the
hash preimage, replay it on startup so state is a function of the log, and delete
the mutable attribute and compute it instead. In cast-verify, add a
unique index on (election_id, serial_hash) where
superseded = false, or wrap the read and write in one serializable
transaction. The correct pattern already exists in
verified-vote/src/blindfold/store.py, where single use is a
PRIMARY KEY insert decided once by the database and tested under
concurrency. Reconciliation caught this at the next layer during the audit, which
is the design working: it reported "ballot style 'p01': 2 counted vs 1
issued."
A related repair in the same family: give each cast an independent random
receipt, and have the checker answer only "a ballot under this receipt is in the
record," never whether a later one superseded it. Same rule as the byte-identical
SPENT and ACCEPTED view, applied to receipts.
The repair, four parts, none of which touches the token format or the hash
structure. BALLOT_BATCH_SEALED with a batch id and a ballot count
and a refusal to release below k, which makes the ordering attack a 1-in-k guess. A
physically shuffled batch at the seal step, attested by the same two officers and
recorded as an event. recorded_time rounded to the batch on every
event downstream of BALLOT_ACCEPTED in every artifact that leaves the
office, with per-event separation timestamps dropped from the public proof API
entirely; the internal log keeps full precision. And a stated minimum reporting
unit, below which mail results roll up to the next unit. Apply 4.3 to
safe-elections-org as well: round tokenIssuedAt to the
day, publish nothing in arrival order, and put the roll and the bulletin board in
different stores.
The repair, and it is half built already. Require a published 0-or-1 proof
per ciphertext and a contest-total proof per race, verified before the ballot
enters the box; store the proofs on the bulletin board; re-verify them in
offlineVerifier.ts as a fifth numbered check. Better still, adopt the
ElectionGuard path, which supplies these proofs as part of the protocol, and the
adapter is already real running code. The Chaum-Pedersen machinery, the
domain-separated Fiat-Shamir hash, subgroup membership by in_group and
the challenge split (c0 + c1) mod q == c are all in the corpus
already.
Four bounded pieces. Replace the trusted dealer with distributed key generation so the full secret never exists in one place - her own
guardians.py already does exactly this, and ElectionGuard implements it
too, so this is wiring rather than invention. Write Tier 0 as an artifact: a script
of record, named guardians from adversarial parties with published affiliations,
two-person integrity on each share, serial-numbered tamper-evident share containers
logged in and out, a witnessed and video-recorded session, a signed transcript
published with the election public key, and an air-gapped machine destroyed or
retained under seal. Write the share destruction procedure, reusing the signed
non-reconstructive destruction record and the "Retired items" table, zeroizing with
the discipline already in src/vault/ceremony.mjs. And apply the
five-criteria independence test to guardians, not only to anchoring custodians.
And put the keys where the design already says they belong. Adopt
src/kms.ts as the only key interface - sign,
getPublicKey, encrypt, decrypt, with
deliberately no generate or import - together with the
unconditional production startup throw.
The repair is already written by her. engine/attest.mjs ships
both freshnessAsShipped(), transcribed exactly so the finding is
demonstrable in the reader's browser, and freshnessCorrected() with a
whole-value timing-safe compare and all-zeros an explicit FAIL, and
/findings.html runs them side by side. Promote
freshnessCorrected() into verify_attestation.py, keep the
shipped version beside it as the published finding, and add a poison case requiring
an all-zero report_data to fail. On the same pass, fix
ruleChainUnbroken to start at step 0 and require an origin step; the
port-diff gate that found it stays as the regression test.
The repair, which she specified in lockchain/kit25/the-record.md
Part 6: publish a periodic hash of the log, never the records, to two or more
independent public witnesses under separate control, at phase boundaries rather
than on a clock. Add a log.head.published entry type and a cosigner
API; add a verify-log.mjs flag that fails when the local head carries
no witness signature after a stated interval, so a failure to publish is a detected
condition rather than a silence; replace in-process witnesses with configured
external keys; adopt the custodian regime for the human half - minimum three
custodians, five written criteria, 24-hour signed acknowledgement, five-business-day
byte-for-byte production, periodic test requests between elections, annual
re-attestation, and the n-1 remediation rule.
Her own resolution is already written: WORM the hashes, never the records.
Object Lock over published tree heads only, the record store stays deletable, and a
deletion is an event in the log rather than a hole in it. Say it explicitly in the
deployment guide. With it: port the doctrine gate into the platform build and
change the two "immutable" strings, and fix
gate-provenance.mjs by vendoring the upstream engine, recording its
SHA-256 and re-hashing it at test time. The provenance claim is true - 45
declarations compared, 43 byte-identical and the 2 that differ are exactly the
deliberately re-namespaced CANONICAL_VERSION and
MERKLE_VERSION - so the only thing missing is a reader's ability to
check it.
Carry custody inside the token and have the ballot box refuse any authority-held
token as a runtime invariant rather than a runbook rule. For mail, make
VOTER_HELD the only permitted value. And treat the print step as a
Tier 1 component under the isolation rule, contract it under the same
custodian-style written criteria, hand it the raw_token once inside the
attested boundary, and reconcile created = mailed + spoiled + retained
from printReconcile() against a physical count of the retained cage at
end of day.
Pin the multiprocessing start method in eg_compat.py, five lines.
Fix the doc drift in demo_full.py, ten minutes, because it is the first
thing a reviewer runs. Fix the published byte count in
provenance.json with Buffer.byteLength, thirty seconds,
and grep for the same trap wherever a size is published next to a hash. Give
anonymitySet() a stated floor below which publication is refused rather
than reported. Move the DP budget and the differencing register into the same
durable append-only store as the log, because a budget an operator can reset by
restarting the process is not a budget. Remove committed key-shaped and state files.
And strip the confidential directories before any handover, because publishing
unfiled invention detail forfeits most foreign patent rights immediately and cannot
be undone by taking a page down.
Independent cryptographic protocol review by a named academic or firm, published: the subject is MBLTS, the LockChain spine, and the guardian tally. And Question 11 first, before any pilot scope is written - is this regulated voting-system equipment under state law - because the answer changes what review, approval and certification steps apply to every other question, and election counsel, explicitly not the system operator or the engineering team, has authority to answer it. Settle at the same time which tiers sit inside the certification boundary, how the attested measurement is recorded in the certification package, and how a rebuild mid-certification is handled.
freshnessCorrected(), keep the shipped
version as the published finding, add the all-zero poison, and fix
ruleChainUnbroken to start at step 0 on the same pass.src/kms.ts as the only key path with the
unconditional production throw, retiring the env-var signing key, the per-process key
cache and the demo keys at once.joinScan() and a refusal threshold on
anonymitySet().A standing rule for every step, from
dexters-lab/docs/decisions.md D6: every check must be seen to fail. Each
gate runs against an honest case that must pass and a poisoned case that must fail, and
a gate that fails to fail is reported VACUOUS and fails the build.
custody-layer/gates/run-all.mjs is the standard to meet: 272 checks, 38 of
38 poisons caught. Source: THE-PROCESS.md sections 7 and 8.
Section 08
Election Day is Tuesday, November 3, 2026, and the countdown below is live. Two federal deadlines land before it. This section says what happens each week, what will be finished by November 3, and what deliberately waits until 2027.
Election Day for the midterms is Tuesday, November 3, 2026. Two other dates govern this schedule harder, because they arrive first and they are federal law.
Federal certification of a voting system takes between six months and more than a year, and systems built to VVSG 2.0 have taken years. That clock cannot be compressed by working harder, and it does not need to be, because most of this design does not sit inside the certified voting system at all. That is the engineering answer to a 68-day deadline: split the program in two by where each part sits relative to certified equipment.
Track A
None of it marks, reads, or tabulates a ballot. It sits alongside the certified system and observes it. Ballot tracking of exactly this kind is already required of states for UOCAVA voters, so the category is established, not novel. Track A is what a county can adopt before November 3.
Track B
These are the deepest parts of the design and they are the parts a certification body must see. Building them on Track B's clock is not a delay; it is what lets Track A ship in 68 days without waiting on anyone. Track B is what makes the count itself verifiable, and it is the 2028 argument.
Every step number refers to the build order in section 07. Steps are already sized; these are the dates they land on. Scroll the timeline sideways.
Stated plainly, because a schedule that hides its assumptions is not a schedule.
Track A ships by the first date. Track B is the 2028 argument, and it starts the week after the midterms with a published cryptographer review. THE-PROCESS.md section 10.4, "The date to put on every slide"
Section 09
Fifty-eight repositories cover this ground. A reviewer needs one answer per question, so this section names which repository is the answer for each part, which are kept only for reference, and which are retired.
Fifty-eight repositories cover this ground with heavy duplication. The duplication is not waste - it is how the good version was found, and the audit could compare three passes at the same platform precisely because all three exist. Going forward a reviewer needs one answer per question.
StrongRoomSE/bb2g-election-os. 70 of 70 modules operational, 25
poison-tested build gates with the VACUOUS verdict, 21 platform checks
plus 56 module checks the reader runs in their own browser, and the only
real-browser smoke test in the family across 31 routes. Brand this one.
kristenslab/election-command. Ed25519 signatures with a key registry and revocation, signed and chained checkpoints with inclusion proofs, four integrity
layers reported separately, eight poison cases one per layer, and
correction-by-append enforced by the absence of any mutator. Keep it as a component
donor rather than a platform.
election-command/assets/crypto.mjs is already sitting in
bb2g-election-os, blob
87f9403b4a9b155f4180dc47d0876146e1da68a1, byte-identical, shipped to
dist/assets/crypto.mjs, and nothing imports it. The strongest crypto in
the family is present in the strongest build and unused. Wiring it in is step
10.
assets/mblts.mjs, 50,382 bytes, 24 of 24
engine tests, and the most reused artifact in the corpus.it() blocks across ten suites, a KMS interface no key crosses,
retention against 52 U.S.C. 20701, and governance documents nothing else
has.test_boundary.py walks every column, and the
vendored-engine re-hash refuses stale vectors.expectedPathLength bound, and
kit25/the-record.md.docs/decisions.md is the most valuable non-code artifact in the
corpus.kristenslab/verified-vote. Keep combineDecryption's
commitment check and the cast_hour truncation reasoning; both belong
in the final process.kristenslab/strongroom. Marked RETIRED already. Lift
docs/hardening.md, the Budget class, the
SDK-as-only-dependency shape and quorum-console.bb2g-election-os. Lift the authorization function and the
currency-engine findings first.safe-elections-isolation. Keep three things verbatim forever: the
one-bit handoff in section 06, the refusal of bulk list matching in section 05
with its error-distribution argument, and
public/audit-report.html.strongroom/src/log/chain.mjs. Keep
sealGuard.ts's refusal to seal a brief with zero claims.election-integrity/engine/wall.mjs and
severance.mjs as the canonical wall and severance sources, and keep
the deliberately imperfect demonstration election.custody-layer for
the instruments. Keep tools/ingest.mjs and
tools/derive.mjs - nothing on a page typed twice - and
tools/compile-internal.mjs.engine/carried/severance.mjs, engine/origin.mjs,
engine/enrol.mjs and server/lib/blindfold.ts, all of
which are named in the component table.ontherecord, conduit,
conduit-monorepo, verifyfirst-teams,
perimeter-gov, claim-verifier. Keep three artifacts
permanently: conduit/audit.py as the smallest correct hash-chained
ledger in the corpus, THREAT_MODEL.md as the shape the mail-ballot
threat model should take, and lib/receipt.ts as the shape of the
published tally artifact.gates/poison.mjs and gates/smoke.py's
_Mark vacuous-pass guard into the election repositories, where they
are needed and currently absent.<script> tag, every page rendered perfectly, and nothing
executed.Superseded by column and
a Retired items table - as the mechanism that governs this whole
consolidation.One rule to keep the consolidation from undoing itself: carry engines
byte-for-byte, hash them at ingest, recompute the hash in the reader's browser, and do
not "fix" a drift by updating the recorded hash. And from
dexters-lab/MANIFEST.md, the rule that governs every claim this program
publishes about itself: the one number to distrust is any claim of the form "N of N
complete." Source: THE-PROCESS.md section 9.
Section 10
Nineteen terms this site uses, each in one sentence, with no mathematics. If a word on this page stopped you, it is defined here.
Every term above is used in the sections of this site, and every mechanism it describes is drawn from THE-PROCESS.md.