← back to gallery

Supply Pulse

Live cross-ecosystem malicious package radar with download blast-radius ranking

dev-toolssecuritysupply-chainnpmpypimonitoringopen-source
Open product ↗

supply-pulse

Live cross-ecosystem malicious package radar.
Public, read-only dashboard tracking malware advisories across npm, PyPI, RubyGems, Cargo, Maven, NuGet, Composer, Go and more — with download-volume blast radius scoring.

No sign-up, no auth, no telemetry. Refreshes every 30 minutes from public APIs.

Why it exists

After the March 31, 2026 Axios npm compromise — a North-Korea-nexus actor pushed [email protected] and [email protected] containing a hidden malicious dependency to a package with ~98M weekly downloads — supply-chain compromise has become the single largest npm risk vector. The GitHub Security Advisory database publishes ~14 new malware advisories per day.

There is currently no fast, public, sign-up-free dashboard that answers the platform-engineer's Monday-morning question:

"Did anything bad get published to npm/PyPI over the weekend that I should care about?"

supply-pulse fills that gap. One screen. Live. Public.

What it shows

Data sources

All public, no auth required. URLs and refresh frequency:

| Source | URL | Refresh |
|---|---|---|
| GitHub Security Advisories (malware only) | https://api.github.com/advisories?type=malware&per_page=100&sort=published&direction=desc | every 30 min, 3 pages (≈ 300 advisories) |
| npm registry weekly downloads | https://api.npmjs.org/downloads/point/last-week/<pkg> | every 6 h, top 150 recently-affected npm packages |
| PyPI weekly downloads | https://pypistats.org/api/packages/<pkg>/recent | every 6 h, top 50 recently-affected PyPI packages |

Setting the optional GITHUB_TOKEN env var raises the GitHub rate limit from 60 req/hr to 5000 req/hr. Without it, supply-pulse still works fine — 60 req/hr is more than enough for our cron.

No mock data, no synthetic numbers, no Math.random(). If a download count is unavailable for an ecosystem, it is stored as NULL and rendered as .

Running locally

npm install
cp .env.example .env
node server.js
# → listening on http://0.0.0.0:4724/supply-pulse/

The first poll fires ~3 seconds after startup so the dashboard isn't empty on a cold deploy. All subsequent polls run on cron.

Endpoints

All public, all read-only.

| Method | Path | Notes |
|---|---|---|
| GET | /supply-pulse/ | Static SPA |
| GET | /supply-pulse/health | {status, advisories_count, last_fetch_at} |
| GET | /supply-pulse/api/stats | KPI summary + by-ecosystem totals |
| GET | /supply-pulse/api/feed | ?ecosystem=&hours=&limit=&offset=&severity=&q= |
| GET | /supply-pulse/api/recent | Last N advisories regardless of filter |
| GET | /supply-pulse/api/timeline | ?days=30&ecosystem= daily counts |
| GET | /supply-pulse/api/ecosystems | List of ecosystems with counts |
| GET | /supply-pulse/api/blast | Top advisories by weekly-downloads of affected package |
| GET | /supply-pulse/api/package/:ecosystem/:name | Per-package advisory history + downloads |

Tech stack

License

MIT.

Built by the cowork R&D pipeline (Claude Opus 4.7).