---
I'm Venezuelan, and for years, knowing what was happening at home meant opening twenty tabs. X to see if the power went out. A Telegram channel for the dollar rate. A seismology site to check whether the quake everyone felt was real. And I never knew if a number was from today or from three days ago.
So I built the tool I wanted. It's called Vigía ("lookout" in Spanish), and it puts the whole country on one screen.
Site and downloads: https://vigia.live
Code: https://github.com/elberacasa/vigia
What it shows
- Money: the official BCV rate next to the P2P market rate, with the gap computed live
- Internet: outages by state and by provider, with history so you can see when a drop started
- Censorship: which sites are blocked, on which provider, and how (from OONI measurements)
- Earthquakes, rain, fires and recent satellite imagery
- News from 224 Venezuelan outlets, including their Telegram channels, grouped by story
- And more: the Guri dam level (most of the country's electricity), the official gazette, health, migration and markets
When two independent sources agree on a blackout, it builds an evidence chain that links both.
The rule that shaped everything: freshness is a value
Most dashboards show a number. Vigía shows a number and its age. Every observation carries two timestamps: when the source says it was true, and when Vigía fetched it. Every adapter has a freshness budget. If a feed misses it, the UI marks the figure as stale instead of quietly showing an old value as if it were live.
Venezuelan data is often late, contradictory or missing, so this matters more than any chart. A dollar rate from Tuesday shown as "now" is worse than no rate at all.
Every source is an adapter with the same contract
There are 313 sources, and 309 of them work with no API key. Each one is an adapter with the same shape:
export interface Adapter<V extends Json = Json> {
readonly id: string;
readonly provider: string; // who publishes the data, e.g. "USGS"
readonly licence: Licence;
readonly keys: readonly string[]; // empty: works with no key
readonly intervalMs: number;
readonly freshness: FreshnessBudget;
/** Network: fetch whatever raw responses one run needs. */
fetch(ctx: FetchContext): Promise<readonly RawResponse[]>;
/** Pure: validate and normalise. Throws on schema violations (the run fails loudly, last-good stays). */
normalise(raw: readonly RawResponse[]): Observation<V>[];
}
Network and parsing are split on purpose. fetch is the only part that touches the internet. normalise is a pure function, so every adapter is tested against recorded real responses, with no network in the tests. The schema is validated at the boundary: when a source changes its format, the run fails loudly and the last good value is kept, shown with its age.
Every observation comes out in the same typed shape: source, a link a person can open, observed time, fetched time, licence, value, optional location and a confidence score. Everything downstream (the panels, the history, the public API) only ever sees that shape.
Numbers are code, never a model
The dollar gap, outage levels, counts and deltas are computed deterministically and tested. There's an optional AI layer, but it only classifies and clusters news stories, and it always links back to the original articles. It never produces a figure.
Built for a cheap phone on a bad connection
A lot of Venezuelans browse on old Android phones with unreliable data, so performance is a feature, not polish:
- The UI is Preact with signals, and the first load is about 84 KB gzipped
- It works offline with the last data it saw
- The server is a single binary built with Bun, 20 to 44 MB depending on the platform, with SQLite storing every observation's history
It runs on your own computer: download, open, done. No account and no central server that can go down or get blocked.
The stack
-
Bun + TypeScript in strict mode (
strict,noUncheckedIndexedAccess,exactOptionalPropertyTypes) -
SQLite (
bun:sqlite, WAL) for history from day one - Zod at every source boundary
- Preact for the UI
- About 990 tests, with CI on Linux, macOS and Windows
- A public API at
/api/v1with OpenAPI, CSV exports, and a terminal mode:curl localhost:7722
Honest limits
- With today's open data, the incident detector misses many regional blackouts, because many states have only one measured internet source. More RIPE Atlas probes inside Venezuela would help a lot, and anyone with a Linux box or a Raspberry Pi that's on all day can run one for free.
- I use it daily on Linux. The Windows and macOS builds pass CI, but nobody has tried them by hand yet.
- It's a beta (version 0.1).
Licence
The code is public under the PolyForm Noncommercial licence. That makes it source-available rather than OSI open source: anyone can use, study, modify and share it for free, but not sell it.
What's next
Right now the optional AI uses Jev, a fast classification model, if you bring a key. If Vigía gets enough users, the plan is to train Laya, our own small model, and ship it free inside the app so everyone gets the smart features without a key.
If you try it, I'd love to hear what breaks, what looks wrong for your state, and which sources are missing. Ideas and votes go here: https://github.com/elberacasa/vigia/discussions/categories/ideas
Top comments (0)