← back to gallery

Gov Security Baseline

Ranks 30+ European governments by TLS, DNSSEC, exposed admins, and trackers.

researchsecuritygovernmentleaderboardtlsdnsseceu
Open product ↗

gov-sec-baseline

European government website security posture — turned into a rankable leaderboard with shareable report-card images.

What this is

The Internet Cleanup Foundation's SecurityBaseline.eu scans ~200K government and public-sector domains across 32 European countries. The data is published, but only as an interactive map — there is no comparison view, no country ranking, no API surface a journalist or sales rep can hit.

This product re-shapes that public scan output into:

No accounts, no auth, no rate limit beyond a 1/min cap on the manual rescrape button.

Data sources & refresh intervals

| Source | URL | Refresh | What we use |
|--------|-----|---------|-------------|
| SecurityBaseline.eu (Internet Cleanup Foundation) | https://securitybaseline.eu/data/config/ and https://securitybaseline.eu/data/report/{layer_id}/{YYYY-MM-DD}/ | Daily at 04:00 UTC (node-cron) | Per-country scan report — TLS, DNSSEC, HSTS, tracking, banner exposure, mail TLS/SPF/DKIM/DMARC, etc. |
| Qualys SSL Labs | https://api.ssllabs.com/api/v3/analyze?host={domain} | On user demand only, cached 24h in SQLite | Live TLS grade for a domain typed into the UI |
| HN Algolia | https://hn.algolia.com/api/v1/search_by_date?query=government+security&tags=story&hitsPerPage=30 | Every 30 min (node-cron) | "Trending on HN" sidebar |

No fake data. If a metric isn't in the source, we don't invent it (e.g. IPv6 — SecurityBaseline doesn't publish it via the report endpoint, so it's dropped and its 5% weight is redistributed across the remaining metrics).

The SecurityBaseline scrape walks all 87 country/region layers in parallel (4 workers, with retries on the previous 1–7 days if today's snapshot is HTTP 500). The first boot kicks the scrape off in the background; you'll see countries populate over ~10–30 seconds.

Composite score

Weighted average on 0–100, missing metrics redistribute proportionally:

Grades: A ≥ 85 · B ≥ 70 · C ≥ 55 · D ≥ 40 · F < 40.

API endpoints

All under /gov-sec-baseline. No auth.

| Method | Path | Returns |
|--------|------|---------|
| GET | /health | {ok:true} |
| GET | /api/status | DB row counts, last-scrape log for each source |
| GET | /api/leaderboard | Ranked array of countries: rank, cc, name, flag, domains, score, grade, four headline metric percentages |
| GET | /api/metrics/list | The metric catalogue (key, label, weight, direction) |
| GET | /api/country/:cc | Country detail: all metrics, rank, worst-scoring 25 domains |
| GET | /api/domain/search?q= | Up to 20 matching domain hosts |
| GET | /api/domain/:host | Single domain's per-flag breakdown |
| GET | /api/tls/check?domain= | On-demand Qualys SSL Labs result (cached 24h) |
| GET | /api/trending | Last ~20 HN stories matching "government security" |
| POST | /api/admin/rescrape | Kicks an out-of-band re-scrape. Rate-limited to 1/min. Public. |
| GET | /api/admin/rescrape/status | Whether a scrape is in flight |
| GET | /share/:cc.svg | 1200×630 SVG report card for the country |
| GET | /country/:cc | HTML wrapper with OG meta pointing at /share/:cc.svg (so Twitter/HN unfurls render) |
| GET | / | SPA |

Running locally

node --version   # requires >= 22
npm install
PORT=4762 node server.js

Then open <http://localhost:4762/gov-sec-baseline/>.

On the very first launch the database is empty — the server kicks off an initial scrape of SecurityBaseline.eu and HN in the background. Reload after ~15s and the leaderboard populates. If you'd rather wait for it before showing the UI, run npm run scrape first.

Environment variables (all optional):

No secrets are required. OPENROUTER_API_KEY / BRAVE_API_KEY appear in .env.example purely because the deployment vault expects every product manifest to advertise them — they are not used.

Out of scope

Architecture notes

Honest known limitations

Data: securitybaseline.eu · TLS audit: Qualys SSL Labs · Trending: HN Algolia.