Skip to main content

actasum

English · Español

Formerly tallylog; renamed on 11 October 2026.

An open format and a verifier for polling-station tally sheets.

After an election, each polling station records its result on a tally sheet. The electoral authority then adds those sheets up. actasum lets anyone repeat that addition from the station-level figures and compare it with the published totals.

It does not handle voting, voter identity or ballots. It looks at the step between the polling station and the published result.

Status: early draft (v0.1). The format will change. The verifier has been run on published files of Spain, Colombia, Mexico and Brazil (below). Do not rely on its output for anything else yet.

Elections run

Every run compares publications of one electoral authority with each other: its station-level files against its own totals. A match means those publications are consistent: the published totals are the sum of the published station figures. It is not a verification. No run has compared the files with paper tally sheets, and a difference does not say which figure is right or why.

All runs are therefore marked run, not verified.

Country Election Stations What matches What does not Status Findings
Spain Congreso, March 2008 59,346 50 of 52 constituencies; 8,111 of 8,112 municipalities Overseas rows of Barcelona and Murcia; one municipality in Soria run, not verified es-congreso
Spain Congreso, June 2016 57,531 52 of 52 constituencies — run, not verified es-congreso
Spain Congreso, April 2019 60,038 52 of 52 constituencies — run, not verified es-congreso
Spain Congreso, November 2019 59,555 52 of 52 constituencies; 8,131 of 8,131 municipalities — run, not verified es-congreso
Spain Congreso, July 2023 60,314 50 of 52 constituencies; 8,131 of 8,131 municipalities Overseas rows of Cantabria and Toledo run, not verified es-congreso
Spain European Parliament, June 2024 58,556, plus 52 overseas rows The country and all 52 provinces — run, not verified es-other-elections
Spain European Parliament, May 2019 60,076, plus 52 overseas rows 38 of 52 provinces; 8,126 of 8,131 municipalities The country: the three levels published do not add up to each other run, not verified es-other-elections
Spain Local, May 2023 57,681 5,248 of 5,272 municipalities 24 municipalities; in 5 the station votes would give other seats run, not verified es-other-elections
Spain Local, May 2019 57,188 5,270 of 5,291 municipalities 21 municipalities; 8 seat allocations to explain run, not verified es-other-elections
Colombia President, 31 May 2026, Bogotá D.C. 17,157 All 16 vote figures of the official result (E-26); 6,410 of 6,410 changes recorded in the AGE agree with the file Stations: 17,164 on the E-24. The file as published is the commission's shifted by 13 bytes; repaired and checked against the hash in the AGE run, not verified co-presidente-2026
Colombia President, 31 May 2026, Córdoba 4,176 All 16 vote figures of the E-26 — (the record of changes is a scan and was not read) run, not verified co-presidente-2026
Mexico President, 2 June 2024 158,972 in the count, 170,621 in the canvass The canvass statements equal an INE press statement in every figure it gives Neither sum equals it: the count has unread figures and is at least 1,883 above for unregistered candidates; the standing sum includes 5,265 ballots of 16 stations whose package was not received run, not verified mx-presidencia-2024
Brazil President, 4 October 2026, first round 499,206 Every vote figure of the 28 reports of the states and abroad, and of the national report One section abroad has no row in the file (its report gives 0 voters) run, not verified br-presidente-2026

In Brazil, 5,246 votes for number 28 are compared as null votes because the official reports declare that they count them so; the verifier says it in its result. The Brazilian reports say their result is subject to change.

How to reproduce each run

No election data is in this repository. Each run starts from files downloaded from the authority; the hash is that of the file as downloaded.

Spain. Per-station ZIP files 02YYYYMM_MESA.zip (Congreso), 07YYYYMM_MESA.zip (European Parliament) and 04YYYYMM_MESA.zip (local), from the Ministry of the Interior's download area.

actasum es replay --mesa 02201911_MESA.zip                       # per constituency
actasum es replay --mesa 02202307_MESA.zip --level municipality  # per municipality
actasum es replay --mesa 07202406_MESA.zip --level province      # per province
actasum es seats  --mesa 04202305_MESA.zip                       # would a difference change a seat?
File SHA-256
02201911_MESA.zip e7b621f295d7bf79b794941d4f11e7bcf7bd971ecbd6eb21dd4269d59926e441
07202406_MESA.zip ed30e4cf11772afb0785f2992f44fa191b8671f933865335b7093ac9ccb6659b
07201905_MESA.zip 8199711babd4b61ee4940469351ab4a263b0cf84a84ccfbe04ab1970a4c1a474
04202305_MESA.zip 20dac696cd2efaa63c9346384ddb682f3da4fda92c6af67447159a60f1ab0de3
04201905_MESA.zip b8b1c649e3841ba735383152fd3a38259a336698d1eb79f7a653cae54c02e204
02200803_MESA.zip, 02201606_MESA.zip, 02201904_MESA.zip, 02202307_MESA.zip not recorded

Colombia. The per-station file (MMV_…csv) and the Acta General de Escrutinio (AGE) of each department's general scrutiny commission, from the Registraduría's scrutiny site. The official totals were typed from the scanned E-26 and are in docs/findings/official/.

# Bogotá: the AGE's text gives the changes and the hash of the original file
pdftotext -layout AGE.pdf AGE.txt
actasum co convert --mmv MMV_XXX_16_000_XXX_XX_XX_XXX_1009.csv --election 2026-05 \
    --age-text AGE.txt --out statements.jsonl
actasum verify --statements statements.jsonl \
    --official docs/findings/official/co-presidente-2026-05-bogota.official.json

# Córdoba
actasum co convert --mmv MMV_XXX_13_000_XXX_XX_XX_XXX_1007.csv --election 2026-05 \
    --sha256 2799c27a52574d85753ee0d31475cebdafd7819f88f1d977fe3b718fc6d636c9 \
    --out statements.jsonl
actasum verify --statements statements.jsonl \
    --official docs/findings/official/co-presidente-2026-05-cordoba.official.json
File SHA-256
Bogotá, MMV_XXX_16_000_XXX_XX_XX_XXX_1009.csv as downloaded 6cec1c68ac67c73407fb1cfd30a337bab08b8b850e6d679c918d98f5e4ec000e
Bogotá, the same file as the commission generated it, as the AGE records dfa5612022c2763666cd72d9553bbcfd5cfcab60076fc4a95889503a34a6ce25
Córdoba, MMV_XXX_13_000_XXX_XX_XX_XXX_1007.csv 2799c27a52574d85753ee0d31475cebdafd7819f88f1d977fe3b718fc6d636c9

Mexico. The INE's PREP and Cómputos Distritales databases of the presidential election. The address they were downloaded from was not recorded. The official totals are typed from INE press statement 368.

# PRES_2024.csv inside 20240603_2005_PREP.zip › 20240603_2005_PREP_PRES.zip, and
# inside 20240608_2030_COMPUTOS.zip › 20240608_2030_COMPUTOS_PRES.zip
actasum mx replay --prep PREP/PRES_2024.csv --computos COMPUTOS/PRES_2024.csv \
    --election 2024-06 --official docs/findings/official/mx-presidencia-2024-06.official.json
File SHA-256
20240603_2005_PREP.zip dcccf23088f81a4ab7c7942a79504b04c4a9347b0a4744f6c328c59fd4d6c31d
its PRES_2024.csv c9d7282e9ba0dabdc6d0378eb3383010ad3f301a26d79c3035a9f2a2fd6d5c45
20240608_2030_COMPUTOS.zip f0b0f90483720374eaac50cfb356b43425697fabfa3f963d22a5b8f2d849312f
its PRES_2024.csv 860c1a539ac98b5def4dff3e82e87a19be3ebba7cb435b680ae79799942d1628

Brazil. The TSE's 28 ballot-box report files bweb_1t_<UF>_051020261403.zip (26 states, the Distrito Federal and ZZ, abroad), from the dataset Resultados - 2026 - Boletim de Urna, and the 29 presidential totalisation reports Relatorio_Resultado_Totalizacao_2026_<UF>.zip (the same 27, plus BR), from https://cdn.tse.jus.br/estatistica/sead/odsele/relatorio_resultado_totalizacao/. actasum br official needs pdftotext (Poppler).

actasum br official --round 1 \
    $(for f in Relatorio_Resultado_Totalizacao_2026_*.zip; do printf -- '--report %s ' "$f"; done) \
    --states-out states.official.json --country-out brasil.official.json
actasum br replay \
    $(for f in bweb_1t_*_051020261403.zip; do printf -- '--bu %s ' "$f"; done) \
    --official docs/findings/official/br-presidente-2026-10-04-states.official.json \
    --official docs/findings/official/br-presidente-2026-10-04-brasil.official.json

The TSE publishes a SHA-512 next to each ballot-box file; the 28 are in docs/findings/br-presidente-2026-1t.sha512 (shasum -a 512 -c checks them). The SHA-256 of each report, and its issue time, are in the two official-totals files above.

What is here

Path Content
docs/spec/core.md Country-neutral format and verification procedure
docs/spec/log.md Append-only log: checkpoints, inclusion and consistency proofs
docs/spec/witness.md Signed checkpoints and witnesses that cosign them
docs/spec/profile-es.md Spain profile: identifiers, data source, known gaps (Spanish translation)
docs/findings/es-congreso.md What the first runs on real data found
docs/findings/es-other-elections.md Runs on two European Parliament elections and two local elections
docs/spec/profile-co.md Colombia profile (draft, one file run)
docs/findings/co-presidente-2026.md Colombia, presidential election of May 2026: Bogotá and Córdoba compared with the commissions' own hash and totals
docs/spec/profile-mx.md Mexico profile (draft): INE PREP and Cómputos Distritales databases
docs/findings/mx-presidencia-2024.md Mexico, presidential election of June 2024: the count as captured and the canvass, station by station
docs/spec/profile-br.md Brazil profile (draft): TSE ballot-box report files
docs/findings/br-presidente-2026.md Brazil, presidential election of October 2026, first round: every state and abroad against the TSE's totalisation reports
schemas/statement.schema.json JSON Schema for a statement
src/actasum/ Reference implementation, Python; standard library only except for signatures
docs/releasing.md How a release is published to PyPI
tests/ Tests on synthetic data

Quick start

Requires Python 3.10 or later. No dependencies, except cryptography for signing and witnesses.

pip install -e .

Download the per-station (_MESA) ZIP file of a Congreso election from the Spanish Ministry of the Interior's download area, then:

actasum es replay --mesa 02201911_MESA.zip

That ZIP carries both the per-station files and the province totals. Pass --tota to read the totals from a separate file instead.

To see in which municipalities a difference arises, compare one level down:

actasum es replay --mesa 02202307_MESA.zip --level municipality

Local elections (04…_MESA.zip) and European Parliament elections (07…_MESA.zip) are read too, with the municipality and the whole country as constituency. Two elections of each type have been run so far; see docs/findings/es-other-elections.md. For a first look at a ZIP:

actasum es describe --mesa 07202406_MESA.zip

A station may have several statements: several captures of the same tally sheet, which must agree, and later acts such as a correction in the general canvass or a repeated vote, which may lawfully differ. The verifier then shows two sums, what the stations counted and what was left standing, and says which one the official totals coincide with (core specification, sections 3.3 and 5.2).

The command prints how many constituencies match and lists every difference. It exits with 0 when everything matches, 1 when there are differences or gaps, and 2 when the input cannot be read.

The same thing in separate steps, producing the neutral intermediate files:

actasum es convert  --mesa 02201911_MESA.zip --out statements.jsonl
actasum es official --tota 02201911_MESA.zip --out official.json
actasum verify --statements statements.jsonl --official official.json

The log

Statements can be loaded into an append-only log. A checkpoint of three lines commits to every entry, and short proofs show that one polling station's statement is in the log, or that the log has only grown.

actasum es convert --mesa 02201911_MESA.zip --out statements.jsonl
actasum log build --statements statements.jsonl --out log-2019-11 > checkpoint.txt

actasum log prove --log log-2019-11 --station es/04/003/02/001/B --out proof.json
actasum log check-proof --proof proof.json --checkpoint checkpoint.txt

Anyone who builds the log from the same ZIP file obtains the same checkpoint. See docs/spec/log.md.

Signing and witnesses

The log can sign its checkpoint, and a witness can cosign it after checking that the log only grew. This needs the optional cryptography package (pip install -e '.[sign]').

# the log operator
actasum key new --name actasum/es/congreso/2019-11 --out log.key      # prints the log's public key
actasum log sign --log log-2019-11 --key log.key
actasum log witness-request --log log-2019-11 --old-size 0 --out request.txt

# the witness, ideally another organization on another machine
actasum witness init --dir witness --name witness.example/w1           # prints the witness's public key
actasum witness trust --dir witness --log-key '<the log public key>'
actasum witness add-checkpoint --dir witness --request request.txt --out cosignature.txt

# back at the log, then anyone
actasum log cosign --log log-2019-11 --signatures cosignature.txt
actasum log check-checkpoint --checkpoint log-2019-11/checkpoint \
    --log-key '<the log public key>' --witness-key '<the witness public key>' --quorum 1

A witness refuses to cosign a log that changed or dropped an entry it had seen. That only means something when the witness is run by someone independent of the log operator; running both yourself shows that the mechanism works and nothing else. Formats follow the C2SP specifications; see docs/spec/witness.md for what is and is not done.

What a result means, and what it does not

  • Matching means the published totals equal the sum of the published station-level figures. That is consistency between two publications, not verification. Nothing more.
  • A difference means they do not add up. It does not say why: lawful corrections during the official canvass, transcription slips and fraud all look the same here. A difference is a reason to ask, not a conclusion.
  • In this first version both inputs come from the same authority. A match shows that its two publications are consistent with each other. It does not show that either reflects the paper tally sheets. Independent captures of the sheets are the subject of later milestones.

Design in one paragraph

A country-neutral core defines the statement, the official totals and the verification rules. A profile per country maps local law and data onto the core. Spain is the first profile because its results are public down to the polling station. Later milestones add an append-only transparency log (a Merkle tree with independent witnesses, as used for web certificates), signed checkpoints, signed statements from accredited observers using W3C Verifiable Credentials, and several independent captures per station.

Roadmap

  1. Format and verifier on a past election, from public data. (run on elections of Spain, Colombia, Mexico and Brazil)
  2. Load a full election into a log and serve inclusion proofs. (first version)
  3. Replay the 2026 Colombian presidential runoff from the published E-14 forms.
  4. A witness that cosigns the log state. (mechanism done; no external witness yet)
  5. Credentials and a capture app, with on-device signing and offline use.
  6. A small real pilot together with a local election-observation organization.

Running the tests

pip install pytest
pytest

Data sources and acknowledgements

  • Election results: Ministerio del Interior (Spain), Área de descargas; Registraduría Nacional del Estado Civil (Colombia), scrutiny site; Instituto Nacional Electoral (Mexico); Tribunal Superior Eleitoral (Brazil), open-data portal. No election data is included in this repository.
  • The column layout of the Ministry's files was first taken from the rOpenSpain infoelectoral R package by Héctor Meleiro, then checked against the Ministry's own layout document. No code from that package is included here.

License

Apache License 2.0.

Metadata

Release files for actasum 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for actasum 0.1.0
File Size Uploaded Provenance
actasum-0.1.0.tar.gz 123.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for actasum 0.1.0
File Interpreter ABI Platform
actasum-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 209.1 kB

Release files / actasum-0.1.0.tar.gz

Download URL actasum-0.1.0.tar.gz
Size 123.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a6e4e93bc53abc4a43e80208f1a05a2803d630da6fa3ebf49e57df5d7bec7519
BLAKE2b-256 checksum
How to use checksums
dd6342f9e5a43bf67efae857dee141da695318be5439db39ea1504c426f9defd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Release files / actasum-0.1.0-py3-none-any.whl

Download URL actasum-0.1.0-py3-none-any.whl
Size 85.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a4421ad6650bba68bc6f301809e4ba456017370b156422d0061679f4afca9304
BLAKE2b-256 checksum
How to use checksums
c30a5db14ef161ad18e0c76f6d9918b4667770a0353b6e1206a1b914f8fade25
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Security for actasum 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for actasum-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page