Operation AngelsBorn Between 2 Generals

CreateConnectProtect

THE PROCESS

Operation Angels · Protect Born Between 2 Generals, LLC

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.

Identity side · proves the person Custody side · tracks the package

Mail-ballot election · end to end

THE PROCESS

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

What this is, in plain words

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.

The idea, in three sentences

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.

Who this is for

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.

What ships by November 3, 2026

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.

Three proof points, as numbers

255 of 257 tests passing in the cryptographic reference implementation, the two skips guarding on an optional dependency, and the design verified against a real published ElectionGuard 1.4.0 ceremony.
38 of 38 deliberately broken cases caught by the custody instrument suite, across 272 checks. Every check has been seen to fail on purpose, which is the only way to know a check is doing anything.
0 of 39 disagreements between her published-record code and the international standard for the same structure, compared at every size from an empty record up to 39 entries.
November 3, 2026 Election Day. Two federal dates land first: ballots to military and overseas voters by September 19, and the domestic mail window from September 24 to October 1.

Every figure is sourced in section 05. Every external date is linked to its governing authority in section 08.

Section 01

The answer in one page

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.

The identity side

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 wall

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

The custody side

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.

The seam

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.

The count

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.

The record

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.

What a voter gets

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.

What an auditor gets, without trusting the county

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 rule when the record and the voter disagree

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

The architecture

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

The thirteen stages

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

    The seam that matters most

    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.

    Why the boundary is correct

    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.

    The handoff, in five steps

    BALLOT ACCEPTED PRIVACY SEPARATION two distinct officers attest TOKEN SPENT terminal state BALLOT_BATCH_SEALED at least k ballots, physically shuffled at the seal step, attested by the same two officers, seal serial recorded TABULATION paper and a batch id only
    1. 1BALLOT_ACCEPTED by an authorized validation authority, a person and not a rule engine. Nothing has been opened.
    2. 2PRIVACY_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.
    3. 3TOKEN_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.
    4. 4BALLOT_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.
    5. 5Tabulation receives paper and a batch id. No token, no digest, no token_id, no IMb serial, no postal reference, no registration key, no per-ballot timestamp.

    Why the batch seal is load-bearing, not decorative

    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.

    Ordering

    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.

    Timing

    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.

    Small-precinct aggregation

    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.

    What remains trusted at this seam, stated plainly

    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

    What is measured, not claimed

    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.

    255 of 257tests passing in 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-vote
    1.4.0real electionguard 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.py
    52independent crypto vectors computed by her own Python and checked against the JavaScript port: 3-of-5 threshold exponential ElGamal with per-guardian Chaum-Pedersen proofs, aggregate-only decryption, rejection sampling rather than modulo bias.safe-elections-isolation proof/crypto-proof.mjs
    0 to 39every leaf count at which the RFC 6962 Merkle implementation was compared against a from-spec MTH, and they agree at every size. Split-view detection treats two validly signed heads of the same size with different roots as unforgeable evidence.verified-vote src/blindfold/bulletin/board.py
    272checks passed, 0 failed, 38 of 38 poisons caught in the custody instrument suite. bb2g-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.mjs
    50,382bytes of the MBLTS engine, byte-identical across three repositories, with 24 of 24 engine tests passing and the vendored copy re-hashed at test time so stale vectors are refused.ballot-trail assets/mblts.mjs
    742c7b0d39d3ca0c34d58c0d60c8371c5373466d5c25a49e0c6ad4d86ab5cc93the sha256 of that engine. One implementation, three homes, no drift possible, with the standing instruction: do not "fix" a drift by updating the recorded hash.bb2g-custody gates/vectors.json
    77checks reproducing all four USPS-B-3200 Rev H Appendix C worked examples at every intermediate step, plus 2,000 pseudorandom round trips. This is what lets a county log be reconciled against something other than itself.custody-layer gates/gate-imb.mjs

    Nine properties most designs never get to

    The phase gate reads the log, not a settings file

    assertPhase(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

    The wrong-share check, in two implementations

    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

    Correction by append, enforced by the absence of a mutator

    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

    A privacy claim tested by trying to break it

    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

    The generated offline verifier

    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 unconditional startup throw

    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

    Refusals recorded as evidence

    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

    The residual gap made legible

    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

    Numbers that refuse to be published unchecked

    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

    Where the components come from

    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.

    RUNNING running code, exercised by tests that were executed during the audit, with the count given RUNNING CODE running code read in full, tests declared in source but not executed in the audit SPECIFIED specified, written
    StageComponent Repo and pathStatus

    Source: THE-PROCESS.md section 4, rows reproduced in full.

    Section 07

    The work plan

    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.

    In priority order

    1. 1

      Supersession as a chained event Half a day plus tests

      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.

    2. 2

      The MBLTS ordering, timing and small-precinct leak One event type, one refusal, one constraint, one publication filter, one procedural line

      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.

    3. 3

      Ballot well-formedness proofs on every tally path Days, not weeks

      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.

    4. 4

      Distributed key generation, the ceremony as an artifact Days for the DKG swap; a document, a form set and a rehearsal for the ceremony

      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.

    5. 5

      Attestation freshness in the isolation gate Hours

      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.

    6. 6

      External witnesses, wired, and nothing forces publication until they are A week for the code; the custodian regime is procurement and can start before it

      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.

    7. 7

      Resolve the WORM contradiction in writing Two-line doctrine changes plus a paragraph in the deployment guide

      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.

    8. 8

      Custody of the token on the casting path, and the print vendor A runtime invariant, and a contract

      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.

    9. 9

      Small, and worth doing on the same pass Five lines to a page each

      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.

    10. 10

      The two external steps that are now worth more than any code Procurement and counsel

      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.

    The build order

    1. Step 1Supersession as a chained event. Half a day plus tests. Append, include in the preimage, derive state from the log, add the negative test and the unique partial index.
    2. Step 2Fix the attestation freshness check. Hours. Promote 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.
    3. Step 3Batch seal, shuffle, coarsened published time. Two to three days. Minimum k plus the matching CHECK constraint, a two-officer shuffle attestation, batch-rounded time in every artifact that leaves the office, a stated minimum reporting unit, then a poison per new refusal.
    4. Step 4Ballot well-formedness proofs. Days. Port the three Chaum-Pedersen proofs, reject at cast time, store proofs on the board, add a fifth numbered check to the offline verifier.
    5. Step 5One key interface, HSM-enforced. A week. Adopt 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.
    6. Step 6Distributed key generation for the guardians. Days if ElectionGuard's ceremony is adopted. Keep both per-guardian checks and keep the disjoint-quorum re-verification as an acceptance test.
    7. Step 7Write the Tier 0 ceremony as an artifact. A document, a form set and one rehearsal.
    8. Step 8External witnesses, wired. A week of code, then paperwork: three custodians under the five written criteria, and the annual re-attestation clock started.
    9. Step 9The privacy test suite. A week. Tests that attempt the prohibited linkages and prove each fails, extended with the ordering and timing attacks as executable attacks, plus joinScan() and a refusal threshold on anonymitySet().
    10. Step 10Consolidate the platform build. Days, because the bytes are already in place.
    11. Step 11Generate the offline verifier for the mail-ballot bundle. A week, with the poison-and-restore acceptance test. A verifier nobody has watched refuse something is a verifier nobody has tested.
    12. Step 12Real-browser smoke and a coverage assertion over every check. Days. This is the only class of check that catches a cryptographic feature present in the source, visible in the interface, and not executing.
    13. Step 13Ship the threat model and the honest report template. A week of writing, with the accepted residual risks section and the open questions for the crypto reviewer.
    14. Step 14Then, and only then, the two external steps. Named cryptographer review, published; and Question 11 answered by election counsel before any pilot scope is written.

    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

    The road to November 3, 2026

    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.

    The one structural fact that shapes the whole schedule

    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

    The evidence layer

    Ships by November 3, 2026
    • Custody tracking
    • Tamper-evident logs
    • The published bulletin board of signed tree heads
    • External witnesses
    • The downloadable offline verifier
    • The privacy test suite
    • Risk-limiting audit support
    • The honest post-election report

    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

    The tabulation path

    Ships on the certification clock: the 2027 primaries and the 2028 cycle
    • Cast-time ballot well-formedness proofs
    • The guardian threshold tally as the system of record
    • Anything else that touches marking or counting

    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.

    Week by week to November 3

    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.

    The fixed dates, which do not move

    Saturday, September 19, 2026 The UOCAVA 45-day wall States must transmit validly requested absentee ballots to military and overseas voters no later than 45 days before a federal election. Nothing that touches issuance, print, or the outbound mail stream can be changed after this date.
    September 24 to October 1, 2026 The domestic vote-by-mail send window Domestic mail ballots go out in this window. Any custody component that tracks an outbound package has to be live and frozen before it opens, which means frozen by the 23rd.
    Tuesday, November 3, 2026 Election Day Publish signed tree heads at each phase boundary with external cosignatures. Custody events recorded. Verifier already in the public's hands, not published afterward.
    September 1, 2026 Massachusetts primary Rehearsal opportunity, week 1. A live observation exercise.
    September 8, 2026 New Hampshire primary Rehearsal opportunity, week 2.
    September 9, 2026 Rhode Island primary Rehearsal opportunity, week 2.
    September 15, 2026 Delaware primary Rehearsal opportunity, week 3. The last live election before the UOCAVA wall.

    What has to be true for this to hold

    Stated plainly, because a schedule that hides its assumptions is not a schedule.

    1. 1Track A is adopted as an observation and evidence layer, not as a replacement for certified equipment. That is what makes 68 days feasible.
    2. 2At least one jurisdiction says yes in Week 1 or Week 2. Track A is real software either way, but a pilot partner is what turns it into evidence about an actual election. If no jurisdiction is in place by September 12, Track A still ships on schedule and runs against the rehearsal election, and the pilot moves to the 2027 primaries with a full year of runway.
    3. 3The ceremony is rehearsed before it is performed. Step 7 is a document, a form set and one rehearsal, and the rehearsal is the part that cannot be skipped.
    4. 4The freeze is respected. The last two weeks are deliberately empty of code. That emptiness is a control, not slack.

    The governing sources for the external dates

    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

    Consolidation: one answer per question

    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.

    The strongest build

    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.

    The strongest cryptography

    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.

    The wiring is already done

    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.

    Canonical: keep, maintain, and point reviewers at these

    • kristenslab/ballot-trail The MBLTS engine, the origin file for the mail-ballot lifecycle. assets/mblts.mjs, 50,382 bytes, 24 of 24 engine tests, and the most reused artifact in the corpus.
    • StrongRoomSE/operation-encrypt-ballot-lifecycle The reference implementation and the specification of record. Closed schema that refuses, about 297 it() blocks across ten suites, a KMS interface no key crosses, retention against 52 U.S.C. 20701, and governance documents nothing else has.
    • StrongRoomSE/bb2g-custody The persisted custody service. 128 tests pass, 54 of 54 poisons caught, test_boundary.py walks every column, and the vendored-engine re-hash refuses stale vectors.
    • kristenslab/custody-layer The instruments that meet the outside world. 272 checks, 38 of 38 poisons; the USPS-B-3200 codec, both Merkle trees with the divergence computed, the NIST schema validated in-browser, the HAVA classifier, the retention clock, and the coercion analysis.
    • kristenslab/verified-vote The cryptographic reference: blind signatures, tally, board, RLA. 255 passed, 2 skipped, 257 collected, and verified against an independent standards implementation.
    • StrongRoomSE/safe-elections-org The working E2E-V tally and the offline verifier pattern. All nine self-test stages pass.
    • kristenslab/safe-elections-isolation The tier model, the attestation gate, the guardian tally. 52 crypto vectors, eight gate groups, eight poisons, 31 live reader-run checks.
    • kristenslab/strongroom The engine: phase gate, log, vault, tokenization, release door. Nothing else in the corpus makes an ordering a property of the system.
    • kristenslab/lockchain The evidentiary spine and the design diary. The hardened inclusion verifier with the expectedPathLength bound, and kit25/the-record.md.
    • StrongRoomSE/counted The disclosure-control and tokenization authority. 83 of 83 tests, and sections 4.3 and 4.4 that close the MBLTS leak.
    • StrongRoomSE/bb2g-election-os The election-operations platform, once the spine is wired in.
    • StrongRoomSE/operation-encrypt The public front door. Leads with what the system will never do, and runs the real engine in the visitor's browser.
    • StrongRoomSE/dexters-lab The workshop: decisions, runbooks, manifest. docs/decisions.md is the most valuable non-code artifact in the corpus.
    • StrongRoomSE/operation-encrypt-current Coordination, release gates, environment isolation.
    • StrongRoomSE/lockchain-record, StrongRoomSE/authorship-record Authorship and priority record: the 25-plus-25 source deposits, the filing pack and the dated commit chronologies.
    • StrongRoomSE/operation-angels-elections, -modules The carried archive, and the manifest-with-omissions pattern.

    Superseded: keep readable, stop developing

    • StrongRoomSE/cast-verify Superseded by kristenslab/verified-vote. Keep combineDecryption's commitment check and the cast_hour truncation reasoning; both belong in the final process.
    • kristenslab/strongroom-engine Superseded by kristenslab/strongroom. Marked RETIRED already. Lift docs/hardening.md, the Budget class, the SDK-as-only-dependency shape and quorum-console.
    • kristenslab/bb2g-election-command Superseded by bb2g-election-os. Lift the authorization function and the currency-engine findings first.
    • kristenslab/verifiable-elections Doctrine now lives in 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.
    • kristenslab/chain-of-command, kristenslab/michael-chain-of-command Superseded by strongroom/src/log/chain.mjs. Keep sealGuard.ts's refusal to seal a brief with zero claims.
    • kristenslab/strongroom-elections, kristenslab/safe-elections, kristenslab/election-integrity Keep election-integrity/engine/wall.mjs and severance.mjs as the canonical wall and severance sources, and keep the deliberately imperfect demonstration election.
    • kristenslab/sealed Superseded by the board for the crypto. Keep the eight-defect prototype audit in the README as a teaching document about how Merkle verifiers fail.
    • kristenslab/provable-custody Superseded by custody-layer for the instruments. Keep tools/ingest.mjs and tools/derive.mjs - nothing on a page typed twice - and tools/compile-internal.mjs.
    • StrongRoomSE/strongroom-se, StrongRoomSE/bb2g-strongrooms Keep engine/carried/severance.mjs, engine/origin.mjs, engine/enrol.mjs and server/lib/blindfold.ts, all of which are named in the component table.

    Archive: no longer part of this program

    • The genesis repos 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.
    • kristenslab/yourvote Not part of the voting system. Move gates/poison.mjs and gates/smoke.py's _Mark vacuous-pass guard into the election repositories, where they are needed and currently absent.
    • kristenslab/keeptherecord Not an election repository. Carry forward the DAYBREAK lesson as a permanent gate: six apps shipped with an unclosed <script> tag, every page rendered perfectly, and nothing executed.
    • StrongRoomSE/strongroom-private-archive Keep as the register itself, and adopt its intake discipline - Public, Internal or Restricted, a named custodian per original, never-reused sequential IDs, a Superseded by column and a Retired items table - as the mechanism that governs this whole consolidation.
    • kristenslab/strongroom-workflow-demo-2026 The newest artifact and a scaffold rather than a build. Keep it as the working environment.

    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

    Glossary

    Nineteen terms this site uses, each in one sentence, with no mathematics. If a word on this page stopped you, it is defined here.

    Token
    A random number that stands in for a ballot package, so the package can be tracked without a name attached to it. It is not derived from anything about the voter.
    Hash, or fingerprint
    A short code computed from a file or a record, where any change to the original, even one character, produces a completely different code, and where you cannot work backward from the code to the original.
    Hash chain
    A list where each entry includes the fingerprint of the entry before it, so removing or editing anything in the middle breaks every entry after it and the break is obvious.
    Merkle tree
    A way of fingerprinting a whole set of records so that one short code stands for all of them, and anyone can be shown that one record is included without being handed the entire set.
    Tree head
    The published summary of a Merkle tree that states both the fingerprint and how many records it covers, so nobody can quietly add or drop entries and still match the published value.
    Digital signature
    A mark only the holder of a particular private key can produce, and anyone can check, that proves who produced a record and that it has not been altered since.
    Blind signature
    A signature on a number the signer never gets to see: your device scrambles the number, the registrar signs the scrambled version, and your device unscrambles it, so the registrar can prove it authorized exactly one credential per voter and still cannot tell which one is yours.
    Credential
    The signed permission that lets one ballot be cast, held by the voter's side rather than stored in a county database. There is no vault of credentials, so there is nothing to steal.
    Ciphertext
    An encrypted value. In this design the encrypted selections can be added together while still encrypted, which is how a total is produced without ever opening an individual ballot.
    Threshold decryption
    Splitting the power to decrypt among several people so that a stated number of them must act together, and no single person can ever do it alone.
    Guardian
    One of the people holding a share of that decryption power, drawn from parties that do not trust each other. Here three of five must cooperate, and only the final total is ever decrypted.
    Distributed key generation, or DKG
    A way for the guardians to create their shares together so that the complete secret key never exists in one place at any moment, not even at the start.
    Bulletin board
    The public, append-only place where encrypted ballots, proofs and published fingerprints are posted, so that observers all see the same record.
    Attestation
    A signed statement that something specific was true at a specific moment, such as a machine running the expected software or two named officers being present at a step.
    Tamper-evident
    Built so that any change leaves a visible mark. Deliberately not the same as "unchangeable," because a record nobody can correct is a record nobody can fix when it is honestly wrong.
    Split view
    The attack where an official shows one version of the record to one audience and a different version to another. Publishing signed tree heads to independent outsiders turns an attempt into permanent proof, because two valid heads of the same size cannot both be right.
    Risk-limiting audit
    Hand-checking a random sample of the paper ballots, with the sample size set by how close the race is, and escalating to a full hand count when the margin demands it.
    Anonymity set
    The number of people a given ballot could have come from. The larger it is the safer the voter, which is why results below a stated minimum group size are withheld rather than published carefully.
    Poison test
    A test that feeds a deliberately broken input to a check to confirm the check actually rejects it. A check that has never been seen to fail is treated here as proving nothing.

    Every term above is used in the sections of this site, and every mechanism it describes is drawn from THE-PROCESS.md.