Corruption in Peru is already public. VigΓa makes it readable. Every tender, contract and debarred company is open data. The bottleneck was never the data, it was the cost of reading it. VigΓa funds that reading with small donations: a contract is read for S/ 3, and the result is public, always.
Investigating one suspicious state contract takes a journalist days: 80-page scanned PDFs spread over a dozen
.gob.pe portals that do not talk to each other. Peru has published about 20 thousand contracts in 2026 alone. Oversight is a
slow, heroic act that does not scale. VigΓa puts AI agents to read the bidding file, cross it with official records and
point at the contracts that deserve a second look, each signal linked to the law it cites and the document it comes from.
Important
We never accuse anyone. A signal is a reason to review a contract, never a charge: not against a company, a person or a party. No verifiable link to an official source means it is not published.
|
One map, three readings: who governs your region, how much is contracted there, and how much VigΓa has read. |
Signals with the law and the evidence, searchable by contract, entity, RUC or region. A read-only MCP server brings it to any AI assistant. |
Fund the reading by zone, by entity or by party and electoral term. Nobody picks which contracts are read. |
Report a stalled work with a photo and a place. Two independent reports in 30 days confirm it. |
flowchart LR
A["ποΈ OECE publishes<br/>a contract"] --> B["π₯ Daily ingestion<br/>contract and documents"]
B --> C[("ποΈ Postgres<br/>one source of truth")]
D["π€ Someone donates<br/>S/ 3 per contract"] --> E["π² Contracts assigned<br/>nobody chooses which"]
C --> E
E --> F["π€ AI agent reads<br/>bases, minutes, contract"]
F --> G{"βοΈ 4 judges<br/>check the result"}
G -->|solid| H["β
Published signal<br/>law and source attached"]
G -->|doubtful| I["π Human review"]
H --> J["πΊοΈ Map, rankings,<br/>donor receipt"]
Each reading ends with four self-evaluation judges (flag support, evidence citation, non-accusatory tone, pipeline completeness). A doubtful result goes to a person instead of being published, and a published result stays public even if it points at the donor who paid for it.
Every contract is linked to the party that governed the contracting entity in the month the contract was called, using the regional and municipal election results of 2014, 2018 and 2022 (JNE). A donor can fund the reading of the contracts of one party in one electoral term, and the contracts are picked at random among those entities.
flowchart LR
J["π JNE results<br/>2014, 2018, 2022"] --> G["gobierno_periodo<br/>who governs each place,<br/>per term"]
E["π’ Entities<br/>by name: Regional Government,<br/>Provincial or District Municipality"] --> EG["entidad_gobierno"]
G --> CP["contrato_partido"]
EG --> CP
C["π Contract<br/>call date"] --> CP
CP --> R["π Ranking and funding<br/>per party and term"]
Note
The link is made with the election winner. Vacancies, recalls and replacements inside a term are not recorded, and
the interface says so. Only entities that are a regional government or a municipality have a party. Ministries and
agencies do not. See 41_aporte_por_partido.sql.
flowchart TB
P(["π₯ People"]) --> W
subgraph CF["βοΈ Cloudflare Workers"]
W["vigia-web<br/>Next.js 14, OpenNext"]
API["vigia-api<br/>Hono, TypeScript"]
DSP["vigia-dispatcher"]
MCP["vigia-mcp"]
REL["vigia-relay<br/>Zurich"]
W --> API
end
subgraph GCP["π¦ Google Cloud"]
PGB["PgBouncer<br/>e2-micro VM"]
SQL[("Cloud SQL<br/>Postgres 16 and PostGIS")]
AG["π€ Agents on Cloud Run<br/>one per contract type, Google ADK"]
VX["Vertex AI<br/>Gemini 3.6 Flash"]
DOC["Document AI"]
RAG["RAG Engine<br/>4 corpora of law"]
JOB["β±οΈ Cloud Run Jobs<br/>and Cloud Scheduler"]
PGB --> SQL
AG --> VX
AG --> DOC
AG --> RAG
end
API -->|"Hyperdrive and tunnel"| PGB
DSP --> AG
AG --> PGB
MCP --> PGB
JOB --> SQL
REL --> OECE
JOB --> OECE
subgraph SRC["ποΈ Public sources"]
OECE["OECE, OCDS API"]
PNDA["PNDA"]
JNE["JNE"]
MEF["MEF"]
ONPE["ONPE"]
end
JOB --> PNDA
JOB --> JNE
JOB --> MEF
AG -.->|traces| ARZ["π Arize Phoenix"]
| Layer | What it does | Where it runs |
|---|---|---|
| Web and API | The site, the read and write API, the dispatcher and the MCP server | Cloudflare Workers |
| Database | One Postgres is the source of truth. Roles per component, read models and the contract assignment functions live inside it | Cloud SQL, behind PgBouncer |
| Agents | One service per contract type (goods, services, works, other). The order of the agents is code, not a model's decision | Cloud Run |
| Ingestion | Contracts every day at 06:00 Lima, documents at 02:00, plus weekly and monthly scrapers | Cloud Run Jobs and Scheduler |
| Relay | The OECE API only answers to nearby IPs, so a Worker in Zurich relays it | Cloudflare Worker |
The raw datasets are not in this repository: they are large and some contain personal data (the RNP registry has
partners' names and IDs). We link the official open-data sources instead. Details in docs/DATA.md.
| Source | Used for | Cadence |
|---|---|---|
| OECE, Contrataciones Abiertas (OCDS) | Contracts, entities, amounts, stage | π’ daily |
| OECE documents | Bidding files and contracts in PDF | π’ daily |
| PNDA (datosabiertos.gob.pe) | Sanctions, entity visits, penalties, interest declarations | π’ weekly to monthly |
| JNE, Infogob | Authorities, candidacies, who governs each place | π‘ monthly and per election |
| MEF | Regional budget | π΄ monthly, the source changed its API |
| ONPE, Claridad | Campaign contributions | π manual, it needs a browser and a Peruvian IP |
| SUNAT (via decolecta) | Company age and status | βͺ on demand |
Warning
Personal data is never shown in clear. IDs and surnames sit behind frosted glass until clicked. Companies and public officials are not censored.
The alert engine, rule by rule
| Rule | What it looks for |
|---|---|
| Sanctioned supplier | A winner with a current OSCE sanction |
| Single bidder | One valid offer at 100 % of the reference value |
| Direct award | Direct hiring without valid grounds |
| Addenda | Changes above 25 % of the original amount |
| New company | A company created shortly before winning |
| Penalties | Suppliers with a history of penalties |
| Revolving door | An official whose declared past jobs include the supplier |
| Campaign money | Contributions to a campaign followed by contracts from the same government |
Each flag cites its legal basis and the official document it comes from. Model-assisted legal and market analysis sits on top of the rules.
|
|
|
| Area | Technology |
|---|---|
| π Web | Next.js 14, React, Tailwind, GSAP, d3-geo. Identity: charcoal and lime, a dot-matrix eye |
| βοΈ API | Hono (TypeScript) on Cloudflare Workers and Cloud Run |
| π€ Agents | Python, Google ADK, Gemini on Vertex AI (3.6 flash for analysis, 3.5 flash-lite for fast steps and judges), Flex pricing |
| π Documents and law | Document AI for PDF text, RAG Engine with 4 corpora (current and historical law, criteria, control) |
| ποΈ Data | PostgreSQL 16 with PostGIS, PgBouncer, Hyperdrive |
| π₯ Ingestion | Python scrapers as Cloud Run Jobs, Cloud Scheduler |
| π Observability | OpenInference, OpenTelemetry, Arize Phoenix |
| ποΈ Infrastructure | Terraform and one deploy script per component |
A reading costs about US$ 0.17 after the Flex pricing and caching work, against a S/ 3 donation per contract.
OpenInference instruments the ADK runner and every Gemini call and exports to Arize Phoenix: a full span tree per contract with tokens, cost, latency, prompts and responses. If an agent drifts, it shows up in the trace.
vigia-peru/
βββ frontend/ Next.js 14: portada, /app (map, contracts, signals, entities, parties, funding), /admin
βββ backend/
β βββ agent/ ADK agents in Python, one per contract type, deterministic pipeline
β βββ api/ Read and write API (Hono) for Cloudflare Workers and Cloud Run
β βββ dispatcher-worker/ Hands paid contracts to the agents
β βββ mcp/ and mcp-worker/ Read-only MCP server
β βββ relay-worker/ Zurich relay to the OECE API
β βββ r2-sync-worker/ GCS to R2 file copy
β βββ db/migrations/ Numbered SQL migrations (01 to 41) and apply_all.py
β βββ scrapers/ Ingestion pipelines: PNDA, OCDS, MEF, ONPE, JNE
β βββ scripts/ Loaders and generators (who governs each place, README charts)
βββ infrastructure/
β βββ terraform/ The platform as code
β βββ deploy/ One script per component, and the project-move runbook
βββ docs/ DATA.md, DATASET_MAP.md, MARCO_NORMATIVO.md, images
Each component ships a .env.example.
# Database: Postgres 16 with PostGIS
docker compose -f infrastructure/docker-compose.yml up -d
python backend/db/apply_all.py # PGHOST=127.0.0.1 PGPASSWORD=vigia by default
# API
cd backend/api && npm install && cp .env.example .env && npm run dev
npm test # unit tests
# Web
cd frontend && npm install && cp .env.example .env.local && npm run dev
# Scrapers
cd backend && pip install -r scrapers/requirements.txt && python -m pytest scrapers/tests
# Regenerate the charts in this README from the live API
python backend/scripts/generar_graficos_readme.pyTip
Deploys go one component at a time, in this order: migration, then API, then web
(infrastructure/deploy/*.sh). Credentials live in environment variables or Secret Manager, never in the repo.
- We never accuse anyone. A signal is a reason to review, never a charge.
- We never publish citizens' personal data. Only serving officials and companies that contract with the State.
- We do not replace the Comptroller, the prosecutors or journalism. We detect and prioritize.
- Citizen reports: two independent reports within 30 days to be confirmed. No photo, not published.
- Funding buys reading, not results: donors do not choose contracts, cannot edit findings, and are published even if flagged.
- Every flag links to official evidence. No verifiable link, not published.
- No ads, no data monetization. Open source and non-profit.
A working platform with national coverage of published contracts (about 20 thousand in the base), the three-reading map, funding by zone, entity and party, public donor profiles and an admin panel. Reading with AI is funded by donations, so the share of contracts read grows as people contribute.
Known gaps: ONPE contributions are loaded by hand, payments are validated manually (there is no gateway yet), and the 2026 election results will be added when the JNE proclaims them.
Spanish documents: ARQUITECTURA.md, SOLUCION.md, OBSERVABILITY.md.
MIT License. Open source, non-profit, no ads, no data monetization.
Built for Peru π΅πͺ