Skip to content
Live2026Solo build — edge function + front end

Threat Radar — a live CTI dashboard at the edge

A static site can't fetch cross-origin threat feeds. An edge worker does it server-side and caches the result, so the upstreams are hit twice an hour regardless of traffic.

Cloudflare Pages FunctionsNext.js (static export)TypeScriptCISA KEVransomware.liveRSS
0
source types
30 min
edge cache
0
API keys

The problem

This site is a static export on Cloudflare Pages — no server, no API routes. A browser on desireddy.com can't fetch most threat feeds directly: CISA's KEV JSON, ransomware.live and the security-news RSS feeds don't send Access-Control-Allow-Origin, so the request is blocked before it starts. And the feeds that do allow it would be hit once per visitor.

The approach

A Cloudflare Pages Function at /api/threat-feed — a small edge worker that lives alongside the static site with no framework adapter. It fetches all the sources server-side (no CORS), parses the RSS itself (the Workers runtime has no DOM), normalises everything to one shape, and returns JSON with a 30-minute s-maxage. Cloudflare caches that at the edge, so the upstreams are hit at most twice an hour per location no matter how much traffic the page gets. No API keys — every source is free and keyless. Nothing is stored, and there's no tracking.

What got built

Three source types behind one endpoint:

  • Actively exploited — the CISA Known Exploited Vulnerabilities catalogue, filtered to recent additions, with the ransomware-campaign flag surfaced.
  • Ransomware & extortion claims — ransomware.live recent victims, with implausible future-dated rows filtered out.
  • Security news — six newsrooms (The Hacker News, Krebs, Dark Reading, The Record, SecurityWeek, SANS ISC), deduped by normalised title, event promos and daily-podcast items filtered.

The front end is a client component with graceful loading and error states, a soft refresh every twenty minutes and on tab refocus. Full dashboard at Threat Radar; a compact version sits in the site header.

What it taught

Pages Functions coexist cleanly with output: export — the functions/ directory is picked up from the repo root and takes precedence over static assets for its route, so you get one edge endpoint without giving up the static build. The edge cache is the whole design: with s-maxage, traffic volume stops mattering to the upstreams.