votaciones-e14

Auditoría ciudadana de las actas E-14 de la Registraduría Nacional
Segunda Vuelta Presidencial — Colombia 2026

Repositorio Video demo Cómo usarlo Integridad Estadísticas

¿Qué es esto?

votaciones-e14 es un pipeline de software libre escrito en Python que permite a cualquier ciudadano descargar masivamente las actas E-14 publicadas por la Registraduría Nacional del Estado Civil, validarlas, convertirlas a imágenes y — lo más importante — verificar que la Registraduría no modifique los archivos después de publicarlos.

Las actas E-14 son los formularios que firman los jurados de votación al cierre de cada mesa, donde se consignan los votos por cada candidato, los votos en blanco, nulos y no marcados. Son la fuente primaria del escrutinio electoral.

Video demo

Recorrido completo del menú interactivo: cómo descargar las actas E-14 de un departamento o de los consulados (voto en el exterior), generar el Excel con la verificación de integridad y detectar cambios entre corridas.

Ver en YouTube

¿Para qué se hizo?

En junio de 2026, durante el proceso electoral de la Segunda Vuelta Presidencial, surgió una polémica pública entre el gobierno y la Registraduría Nacional sobre la integridad de los formularios E-14. El gobierno denunció que podrían estarse modificando actas después de publicadas; la Registraduría afirmó que su sistema de candado hash y estampilla de tiempo garantizaba la integridad de cada archivo.

Este proyecto nació para resolver esa pregunta de forma técnica, verificable y reproducible: si descargas hoy un acta y mañana la Registraduría la cambia, el pipeline lo detecta automáticamente comparando los hashes oficiales de Amazon S3 (donde están almacenadas) entre las dos corridas.

Importante: esta herramienta no acusa ni absuelve a nadie. Solo proporciona evidencia técnica reproducible. La conclusión la saca quien analiza los reportes.

Dimensión del proceso electoral

Datos del catálogo oficial de la Registraduría (allTransmissionCodes.json), capturados al ejecutar el pipeline. Estas cifras describen el universo de mesas sobre las que actúa la herramienta.

Mesas de votación nacional
34
Departamentos + Consulados
1.189
Municipios y sedes consulares
~6 GB
Tamaño estimado del dataset nacional

Mesas por departamento

Cantidad de mesas de votación registradas en cada departamento. Útil para dimensionar el esfuerzo de descarga: a más mesas, mayor tiempo y espacio en disco.

Cómo se determina si un acta está disponible

El catálogo público de la Registraduría agrupa las mesas en dos buckets internos (status3 y status11) cuyo significado preciso no está documentado públicamente. En pruebas hechas con este pipeline, ambos buckets contienen actas que sí se descargan correctamente.

Por eso el pipeline no asume nada del bucket: simplemente intenta descargar cada acta y deja que el servidor responda. Las actas que devuelven un PDF válido se guardan, las que devuelven error o HTML se marcan como faltantes para reintentar más tarde.

El indicador real de disponibilidad es el resultado del paso 2 (descarga), no un conteo derivado del catálogo. En el departamento del Cauca (caso piloto), por ejemplo, de 3.271 mesas en el catálogo se descargaron exitosamente 2.486 actas en una sola corrida.

¿Cómo funciona?

El pipeline tiene 5 pasos accesibles desde un menú interactivo:

1

Descargar índices

Baja el catálogo maestro de mesas en JSON (~36 MB).

2

Descargar PDFs

Descarga en paralelo todas las actas de un departamento o de los consulados (voto en el exterior).

3

Excel + integridad

Calcula MD5/SHA-256 locales y consulta los hashes oficiales del servidor. También funciona sin descargar PDFs (línea base nacional).

4

PDF a imagen

Convierte los PDFs a JPG/PNG para análisis con OCR / IA de visión.

5

Detectar cambios

Compara dos corridas del paso 3 y reporta archivos modificados.

Modo nacional (línea base de los 34 territorios)

El menú interactivo ofrece una opción especial *** TODOS - Inventario nacional *** que ejecuta el paso 3 sobre los 34 departamentos (incluyendo consulados) sin descargar los PDFs — solo captura los headers de integridad oficiales. Es la forma más rápida y liviana de tener una línea base criptográfica nacional de las ~118.000 mesas:

También hay una opción intermedia TOP 4 - Bogotá, Antioquia, Valle, Cundinamarca que cubre el 50% de las mesas nacionales en ~1 hora.

Garantías de no perder evidencia

ArchivoComportamiento
manifest_<DEPTO>.jsonlAppend-only. Cada corrida solo agrega líneas con timestamp UTC. El historial completo siempre crece.
inventario_<DEPTO>.xlsxSe respalda automáticamente a reportes/<DEPTO>/backups/ antes de sobrescribir.
cambios_<DEPTO>.xlsxSe respalda automáticamente antes de sobrescribir.
PDFs descargadosIdempotente — los archivos ya descargados nunca se re-descargan ni borran.

Stack técnico

ComponenteTecnología
Portal de origenAngular SPA + Akamai CDN
Almacenamiento del archivoAmazon S3 con versionado
Lenguaje del pipelinePython 3.10+
Descargas paralelasrequests + ThreadPoolExecutor
Render PDFPyMuPDF + Pillow
Reportesopenpyxl (Excel multi-hoja)
Interfazquestionary (CLI interactiva)
HTTP clienterequests + curl_cffi (impersona TLS de Chrome 131 para evadir Akamai)
VisualizacionesChart.js 4 vía jsDelivr CDN
IconosBootstrap Icons 1.11 vía jsDelivr CDN
DistribuciónScript Python o ejecutable .exe (PyInstaller)

Verificación de integridad oficial

La Registraduría almacena las actas en Amazon S3 con versionado activado. En cada descarga, el servidor devuelve en los headers HTTP tres valores que constituyen la huella técnica oficial del archivo:

Header HTTPQué garantiza
ETagMD5 oficial del archivo en S3 (cambia si modifican el contenido)
Last-ModifiedTimestamp de la última subida al bucket
x-amz-version-idIdentificador único de versión S3 (cambia si re-suben)

El paso 3 captura los tres valores cada vez que se ejecuta y los guarda en un manifest_<DEPTO>.jsonl append-only con timestamp UTC. El paso 5 compara las observaciones entre corridas y reporta cualquier cambio.

Limitación honesta: el CDN cachea los archivos hasta 7 días. Una modificación reciente puede tardar en propagarse a todos los edge nodes. Por eso conviene correr el paso 3 varias veces, en momentos distintos del día.

Cómo el pipeline evade Akamai Bot Manager

El portal de la Registraduría está protegido por Akamai Bot Manager, que en 2026 bloquea masivamente clientes Python estándar mediante TLS fingerprinting (JA3/JA4). La biblioteca requests tiene un fingerprint reconocible como "biblioteca Python" sin importar qué headers envíe — Akamai lo detecta y sirve HTML genérico de "Access Denied" con HTTP 200, sin entregar cookie de sesión.

La solución integrada en el pipeline (6 capas combinadas):

TécnicaQué hace
curl_cffi + impersonate="chrome131"Replica el TLS Client Hello exacto de Chrome 131: mismo cipher suite ordering, extensiones TLS, ALPN. Akamai no puede distinguirlo de un navegador real.
Rate limit global de 3 req/sToken bucket compartido entre todos los workers. Independiente del paralelismo, el ritmo total sostenido nunca supera 3 req/s — Akamai lo tolera como tráfico "humano-like" y no marca la IP.
Workers limitados (4 default)El rate limiter hace irrelevante subir workers. 4 es suficiente para HEAD requests cortos.
Chunks de 100 mesas con detección en vivoMide tasa de bloqueo por chunk. Solo ofrece cambio de red tras DOS chunks consecutivos con >80% bloqueo (Akamai a veces tiene picos transitorios que se autocorrigen).
Cookie ak_bmsc persistida en la sesiónDespués del warmup en /, todas las consultas reutilizan la cookie del Bot Manager Standard Cache.
Cache de observaciones <24hSi una mesa ya tiene ETag capturado en el manifest, no se reconsulta — sobrevive a interrupciones sin perder progreso.

Resultados medidos (validados en producción):

SetupTasa de bloqueoVelocidad
requests + 16 workers96 %~3 req/s (con cambios de red constantes)
curl_cffi + 4 workers (sin rate limit)44–91 %~6 req/s (inestable)
curl_cffi + 4 workers + rate limit 3 req/s0 %3.0 req/s sostenido

Con el setup completo, el barrido nacional (~118.000 mesas) toma aproximadamente 11 horas continuas sin intervención manual. Es lento pero consistente: déjalo correr en background y al día siguiente tienes la línea base criptográfica nacional completa. El cache permite interrupciones y reanudaciones sin perder progreso.

Tiempos medidos en producción

Estimación a velocidad sostenida de 3 req/s: tiempo_minutos = mesas / 180. Los datos son reales, capturados durante el barrido del 22 de junio 2026.

Configuración de las pruebas
Workers (hilos paralelos)4 (de 20 disponibles en el PC)
Rate limit global3 req/s (token bucket compartido)
Tamaño de chunk100 mesas
Cliente HTTPcurl_cffi impersonando Chrome 131
RAM ocupada~210 MB (proceso completo)
CPU usada<5 % (rate limit, no CPU bound)

Workers ≠ velocidad. El rate limit global de 3 req/s actúa como techo independiente del paralelismo. Con 4 workers a 0.75 req/s c/u suma 3 req/s totales. Subir a 8 o 16 workers da la misma velocidad (cada uno solo esperaría más). Esta arquitectura es la garantía de no irritar a Akamai.

Departamento Mesas Tiempo estimado Tiempo medido (4 workers)
Vaupés6020 s20 s
Vichada11940 s
Caldas2.47614 min
Cauca3.27118 min
Atlántico5.98933 min33 min
Valle10.8731 h
Antioquia15.4021 h 25 min1 h 25 min
Bogotá D.C.17.0011 h 35 min
Nacional (los 34)117.959~11 horas
Importante: esta técnica solo accede al contenido público que la propia Registraduría ya publica en su portal. No bypasea autenticación ni accede a información privada. Lo único que evita es que el filtro anti-bot bloquee descargas ciudadanas masivas que técnicamente son legítimas.

Instalación y uso

Opción A — Para usuarios (la más fácil)

Descarga el ejecutable de la última versión y haz doble clic. No requiere Python instalado ni ningún paso adicional — el ejecutable trae todo embebido.

# 1. Descarga votaciones.exe desde:
#    https://github.com/jsnavarroc/votaciones-e14/releases

# 2. Doble clic en votaciones.exe (o desde terminal):
votaciones.exe
El .exe es autosuficiente. Trae el intérprete Python y todas las dependencias dentro. Pesa unos 40 MB y corre en cualquier Windows 10/11. Lo único que necesita es conexión a internet (descarga las actas del portal de la Registraduría).

Opción B — Para desarrolladores

Si quieres leer el código, modificarlo o contribuir mejoras:

# Requiere Python 3.10+
git clone https://github.com/jsnavarroc/votaciones-e14.git
cd votaciones-e14
pip install -r requirements.txt
# requirements.txt incluye curl_cffi (impersona TLS de Chrome para evadir Akamai)
python -m votaciones.menu

Opción C — Generar tu propio .exe

Si quieres compilar el ejecutable tú mismo (por ejemplo para firmarlo con tu propio certificado):

pip install pyinstaller
python build_exe.py
# Resultado: dist/votaciones.exe

Una vez abierto, el menú te guía

==================================================
  Pipeline E-14 - Segunda Vuelta Presidencial 2026
==================================================

? Departamento:
  > *** TODOS - Inventario nacional (linea base de los 34 territorios) ***
    *** TOP 4 - Solo grandes: Bogota, Antioquia, Valle, Cundinamarca ***
    -----
    01  ANTIOQUIA       mun=125  (sin descargar)
    11  CAUCA           mun= 42  (2486 PDFs descargados)
    ...

? Accion para CAUCA (11):
  > 1) Descargar PDFs
    2) Generar Excel + integridad
    3) Convertir PDFs a imagenes
    4) Detectar cambios
    5) Cambiar departamento
    0) Salir

Comandos avanzados (modo CLI)

Si prefieres saltar el menú y ejecutar pasos individuales — útil para automatización con cron, GitHub Actions o scripts batch:

# 1. Descargar el catalogo de mesas (una sola vez)
python -m votaciones.indices

# 2. Descargar todos los PDFs de un departamento o de los consulados.
#    Ejemplos:  11 = CAUCA   16 = BOGOTA   88 = CONSULADOS (voto exterior)
python -m votaciones.descargar 11                          # todo Cauca
python -m votaciones.descargar 88                          # voto en el exterior
python -m votaciones.descargar 11 --prueba 5               # modo prueba
python -m votaciones.descargar 11 --workers 16             # mas paralelismo

# 3. Generar el Excel de inventario + capturar integridad del servidor
#    (incluso si no descargaste los PDFs primero, captura igual los headers
#     ETag/version-id/Last-Modified -> ideal para linea base nacional)
python -m votaciones.inventario 11
python -m votaciones.inventario 11 --workers 16
python -m votaciones.inventario 11 --sin-red               # solo validacion local

# 4. Convertir PDFs a imagenes (para mandar a OCR / IA de vision)
python -m votaciones.imagenes 11 --stitch --horizontal --jpg --rotar 90 --dpi 250

# 5. Detectar cambios entre dos corridas del paso 3
python -m votaciones.cambios 11                            # reporte completo
python -m votaciones.cambios 11 --solo-cambios             # solo modificados

Códigos de departamento y consulados

Algunos códigos comunes que pasarás como argumento a los comandos. El código 88 agrupa todos los consulados donde se vota en el exterior — el pipeline los procesa exactamente igual que cualquier departamento.

CódigoTerritorioCódigoTerritorio
11CAUCA01ANTIOQUIA
16BOGOTÁ D.C.31VALLE
15CUNDINAMARCA27SANTANDER
03ATLÁNTICO05BOLÍVAR
88CONSULADOS (voto en el exterior — 67 sedes)...y 26 departamentos más

Lista completa: ejecuta el menú y verás los 33 departamentos + consulados con flechas.

Mantenimiento abierto

Proyecto iniciado por @jsnavarroc

Este pipeline es un punto de partida. La idea es que la comunidad de desarrolladores lo audite, lo mejore, lo extienda a otros departamentos, le agregue detección automática de enmendaduras, OCR de los votos, dashboards en tiempo real, o cualquier otra cosa útil para la transparencia electoral.

Está publicado bajo licencia MIT: código abierto, libre de usar, modificar y redistribuir. Cualquier ciudadano puede ejecutar el pipeline, verificar los resultados o contribuir mejoras vía pull request en GitHub.

Contribuir en GitHub   Reportar problema