PAN-RAP Municipio v0.5
Anonymous voting system with cryptographic integrity for municipal civic governance. Blind Schnorr over secp256k1, modular HMAC eligibility, convocatoria lifecycle, and verifiable PDF minutes generation.
What is it
PAN-RAP (Planet Assembly Network / Red Asamblearia Planetaria) is a decentralized civic governance ecosystem designed for municipalities of ~25,000 inhabitants. This repo is the reference implementation of the anonymous voting module.
Stack:
- Backend: Python 3.12+ / FastAPI / coincurve (libsecp256k1)
- Frontend: JavaScript ES modules / @noble/curves vendored (no CDN)
- Crypto: Blind Schnorr over secp256k1 (65-byte token)
- DB: SQLite (WITHOUT ROWID on ballot box)
- License: AGPL-3.0-or-later
What it delivers (Phase 5.5 completed)
| Feature | Status | Detail |
|---|---|---|
| Blind Schnorr Engine | ✅ | Issuer (coincurve) + Ballot Box (fail-closed verification) + Frontend (@noble/curves) |
| Modular Eligibility | ✅ | 4 modes: census (HMAC), in-person (single-use codes), endorsement (K endorsements), eid (placeholder) |
| Convocatoria Lifecycle | ✅ | Validated transitions: draft → scheduled → emission → voting → closed → scrutinized → archived |
| Public Scrutiny | ✅ | GET /convocatorias/{id}/escrutinio with tally, turnout, ballot box hash |
| Full Ballot Box Download | ✅ | GET /convocatorias/{id}/urna votes ordered by r_prima |
| PDF Minutes | ✅ | POST /convocatorias/{id}/acta with reportlab, only in scrutinized state |
| Independent Verifier | ✅ |
scripts/verificador_independiente.py audits complete ballot box |
| Tests | ✅ | 10 passed (3 eligibility + 7 lifecycle) |
What it does NOT do yet
| Phase | Status | Pending |
|---|---|---|
| Phase 6 | ⏳ | External cryptographic audit (Cure53, Trail of Bits, etc.) |
| Phase 7 | ⏳ | Real municipality pilot deployment |
| Memory state persistence | ⏳ | In-person codes and endorsements lost on server restart |
| List convocatorias endpoint | ⏳ | Public GET /convocatorias for discovery |
| Full eid mode | ⏳ | OIDC / national digital identity integration |
Transparency note: The project is in Phase 5.5. Tests pass (10/10), the crypto engine is internally audited, but no external audit has been performed. Not suitable for binding use until Phase 6 completes.
How to try it
# 1. Clone and setup
git clone https://github.com/amurlaniakea/pan-rap-municipio.git
cd pan-rap-municipio
python3.12 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # includes reportlab for PDF minutes
# 2. Run tests
pytest tests/ -v
# → 10 passed
# 3. Start server
export PANRAP_EMISOR_KEY_HEX="$(openssl rand -hex 32)"
fastapi dev main.py
# 4. Quick flow (census mode example)
curl -X POST http://localhost:8000/convocatorias \
-H "Content-Type: application/json" \
-d '{"titulo":"Test","texto":"...","opciones":["a_favor","en_contra"],"modo_elegibilidad":"padron","nivel_garantia":"alta","emision_abre_en":"2026-11-01T00:00:00","voto_abre_en":"2026-11-02T00:00:00","cierra_en":"2026-11-03T00:00:00"}'
# → {"id": "uuid", "estado": "borrador"}
curl -X POST http://localhost:8000/convocatorias/{id}/censo \
-H "Content-Type: application/json" \
-d '{"identificadores": ["dni_123"]}'
curl -X PATCH http://localhost:8000/convocatorias/{id}/estado \
-H "Content-Type: application/json" \
-d '{"estado": "programada"}'
curl -X PATCH http://localhost:8000/convocatorias/{id}/estado \
-H "Content-Type: application/json" \
-d '{"estado": "emision"}'
# 5. Verify ballot box and generate minutes
python scripts/verificador_independiente.py
# → VERIFICATION SUCCESSFUL: X valid votes, 0 invalid
Security Architecture (summary)
-
S1 Sequential emission:
threading.Lock()+ TTL 10s -
S4 Zero int arithmetic on secrets: only
coincurve -
S5 Ephemeral
kper session:PrivateKey()new eachinicio() -
S6 Fail-closed validation:
urna_schnorr.verificar()catches exceptions -
S7 Key outside git:
.gitignorecoversclave_emisor.hex -
S8 Vendored client:
frontend/lib/with@noble/curves(no CDN) -
Eligibility:
persona_ref = HMAC-SHA256(secreto_convocatoria, convocatoria:identificador)— different per convocatoria, non-linkable -
Persistent secret: stored in
convocatorias.secreto_hmac, never exposed in HTTP responses
References
- Repo: https://github.com/amurlaniakea/pan-rap-municipio
- README: https://github.com/amurlaniakea/pan-rap-municipio/blob/main/README.md
-
Docs:
docs/01-spec-v0.md,docs/03-motor-blind-schnorr.md -
Tests:
tests/test_elegibilidad.py,tests/test_ciclo_vida.py -
Frontend:
frontend/voto_schnorr.js,frontend/lib/noble/ -
Verifier:
scripts/verificador_independiente.py - License: AGPL-3.0-or-later (661 verbatim GNU lines)
Author: Pedro Sordo Martínez
License: AGPL-3.0-or-later
Top comments (0)