sandbox-pulse
Live momentum, maturity, and security tracker for AI agent code-execution sandbox runtimes — E2B, Daytona, Modal, Cloudflare Sandboxes, Vercel Sandbox, Beam Cloud, Blaxel, Northflank, RunLoop, freestyle.sh, Sprites.dev, CodeSandbox SDK.
Every AI coding agent now externalizes code execution to a sandbox provider. The market exploded in the last twelve months and there is no neutral place to compare these providers on real ecosystem signals. Sandbox Pulse fixes that by tracking — daily — GitHub stars, npm/PyPI download counts, GHSA advisories, GitHub releases, and Hacker News mention counts. Everything is sourced from a public API at runtime. There is no mock data and no seed metric values anywhere in the codebase.
Why this exists
Pick-a-sandbox blog posts go stale within a month. Cold-start, pricing, isolation model, and GPU support change quietly. We don't try to benchmark every provider ourselves (we'd be wrong half the time); instead we surface the leading indicators that anyone can verify:
- GitHub stars — coarse but trustworthy signal of developer attention. We track the delta over 7 and 30 days.
- Open issues and pushed_at — how alive is the repo.
- GitHub releases — cadence of shipping.
- GHSA advisories — published security advisories on the official repo.
- npm / PyPI weekly downloads — closer to actual usage than stars.
- HN mention count (last 30 days) — community attention right now.
A composite Pulse Score (see formula below) rolls these into a single 0–100 number that's good for at-a-glance comparison and bad for major life decisions.
No mock data
The codebase has zero seed metric values, zero Math.random() jitter, zero "preset realistic numbers". The only static fields are editorial: provider name, homepage URL, isolation model, GPU support, max session length, and pricing description with a pricing_verified date. The UI explicitly labels these as editorial.
If a provider's repo 404s, we mark it inactive and stop fabricating values. If a GitHub call fails, we log it to fetch_log and show — in the UI rather than zero.
Pulse Score formula
pulse = 0.30 * z(stars_7d_delta)
+ 0.20 * z(downloads_7d_delta)
+ 0.20 * z(hn_mentions_30d)
+ 0.15 * recency(latest_release_days_ago, half-life = 30d)
+ 0.15 * (1 - critical_advisories_open / total_advisories)
z() is the z-score across the currently-active providers in that batch. Missing terms are excluded from the normalization rather than zero-filled — a null metric should not punish a provider the same as a real zero. The final number is clamped to ±3σ and rescaled to 0–100. The formula is also exposed at /api/sources so the math is part of the product, not a comment in a file no one reads.
Data sources
| Source | URL pattern | Provides | Frequency |
|---|---|---|---|
| GitHub repo | https://api.github.com/repos/{owner}/{repo} | stars, forks, open issues, watchers, pushed_at | every 6h |
| GitHub releases | https://api.github.com/repos/{owner}/{repo}/releases?per_page=5 | tag, published_at | every 6h |
| GitHub advisories | https://api.github.com/repos/{owner}/{repo}/security-advisories | ghsa_id, severity, summary | every 6h |
| npm registry | https://api.npmjs.org/downloads/point/last-week/{pkg} | weekly downloads | every 12h |
| pypistats | https://pypistats.org/api/packages/{pkg}/recent | weekly + monthly downloads | every 12h |
| HN Algolia | https://hn.algolia.com/api/v1/search_by_date?query={kw}&tags=story&numericFilters=created_at_i>{ts} | mention count, top story titles | every 12h |
All endpoints are unauthenticated. A GITHUB_TOKEN env var is supported but optional — without it we stay under the 60 req/h anonymous cap by batching 12 providers every 6h (= 48 req/day).
Stack
- Node.js 20+ (uses native
fetch) - Express 4
- better-sqlite3 (WAL mode)
- node-cron
- helmet + compression
- Vanilla JS SPA (no build step)
Running locally
npm install
cp .env.example .env
# optional: edit .env to add a GITHUB_TOKEN
npm start
# then open http://127.0.0.1:4839/sandbox-pulse/
The server runs a bootstrap refresh five seconds after startup so the first user sees real data without waiting for the next cron tick.
Endpoints
| Route | Auth | What |
|---|---|---|
| GET /sandbox-pulse/ | none | SPA shell |
| GET /sandbox-pulse/health | none | { ok, providers, lastRefresh } |
| GET /sandbox-pulse/api/providers | none | All providers with current snapshot and deltas |
| GET /sandbox-pulse/api/provider/:slug | none | One provider with history, releases, advisories, HN stories |
| GET /sandbox-pulse/api/rankings | none | Top-N by stars-7d, downloads-7d, HN-30d, pulse |
| GET /sandbox-pulse/api/timeline | none | Recent releases + advisories across all providers |
| GET /sandbox-pulse/api/sources | none | Transparency: every data source URL and last fetch timestamp |
Every endpoint is public. No Basic Auth, no API key, no admin panel.
Adding a provider
Edit fetchers/providers.js, append a new entry with:
slug— stable URL-safe identifiername— display namehomepage— provider homepagegithub_repo—owner/repoof the official SDK or core reponpm_pkgand/orpypi_pkg— official client package, if anyhn_keyword— search keyword for HN buzzpricing_note+pricing_verified— editorial blurb + ISO dateisolation,gpu_support,max_session_minutes— editorial categorization
Restart the server — seedProviders() runs on boot and upserts every entry. The next cron tick (or npm run refresh) populates the metrics. Do not put numeric metrics in this file.
License
MIT.
Credits
Built by Holy AI on Cowork with Claude Opus 4.7. Trend research drew on May 2026 coverage of the agent-sandbox market — see the spec for sources.