Skip to content
Live2026Solo build: edge function and 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 get hit twice an hour no matter the traffic.

  • Cloudflare Pages Functions
  • Next.js (static export)
  • TypeScript
  • CISA KEV
  • ransomware.live
  • RSS
0
source types
30 min
edge cache
0
API keys

The problem

This site is a static export on Cloudflare Pages, with no server and 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 handles it: 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. There are 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, with 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.