swarm-board
Live comparison of parallel AI coding agent runners — pick the right harness for fan-out coding.
A new product category emerged in early 2026: tools that spawn N coding agents in parallel on isolated workspaces (git worktree / container / VM) and gate their PRs before merge. swarm-board is the buyer's guide for this space, with one row per runner and real public signals — GitHub stars, license, isolation method, review gate, multi-model support, latest release.
What you get
- Board view — sortable / filterable table of 20+ runners (OSS + SaaS). Filter by kind, isolation method, license, review gate. Sort by stars, 7d Δ, 30d Δ, latest release, latest commit.
- Detail view — per-runner page with live metrics, feature matrix, stars sparkline, latest release notes, source-of-truth links.
- Movers feed — top star gainers in a rolling 7- or 30-day window, based on daily snapshots.
- Pick wizard — 3-question filter ("self-hosted?", "free?", "review gate?") narrows the field to a short list of matches.
- /health endpoint — auth-free, always 200.
Stack
- Node.js 20+ with global
fetch - Express 4 mounted at
BASE_PATH(default/swarm-board) - better-sqlite3 with WAL journaling for persistent snapshots
- node-cron for scheduled GitHub / SaaS refresh + daily snapshot
- helmet + compression for production-grade hardening
- Vanilla JS SPA — no build step, no framework, dark theme
Quick start
git clone <this-repo>
cd swarm-board
cp .env.example .env # set GITHUB_TOKEN for higher rate limits (optional)
npm install
npm start
# Open http://localhost:4913/swarm-board/
The first cron run will populate signals from GitHub and the vendor pricing pages. The board renders immediately on cold start using the curated seed metadata in data/runners.seed.json; live signals fill in within 30 seconds.
Environment variables
| Variable | Default | Notes |
|---|---|---|
| PORT | 4913 | HTTP port |
| BASE_PATH | /swarm-board | URL prefix where the app is mounted |
| DB_PATH | /var/www/projects/swarm-board/swarm-board.db | SQLite file path (WAL journal is co-located) |
| GITHUB_TOKEN | _unset_ | Optional PAT; bumps GitHub REST rate limit from 60/h to 5000/h |
| GITHUB_REFRESH_CRON | 15 /3 | When to refresh OSS rows |
| SAAS_REFRESH_CRON | 45 /12 | When to refresh SaaS rows |
| SNAPSHOT_CRON | 0 6 * | When to write the daily star / fork / issue snapshot |
| FETCH_TIMEOUT_MS | 12000 | Per-request timeout for every outbound HTTP call |
| HTTP_USER_AGENT | swarm-board/1.0 (+...) | Sent on every outbound request |
Data sources & refresh cadence
| Source | Used for | Refresh |
|---|---|---|
| https://api.github.com/repos/{owner}/{name} | Stars, forks, open issues, license, default branch, last push | every 3h |
| https://api.github.com/repos/{owner}/{name}/releases/latest | Latest release tag, date, URL, body excerpt | every 3h |
| https://raw.githubusercontent.com/{owner}/{name}/{branch}/README.md | Parses isolation method, review-gate posture, multi-model and max-worker hints | every 3h |
| Vendor pricing / homepage HTML | Pricing summary string, plus parser pass when no GitHub repo exists | every 12h |
| Local SQLite snapshots | 7-day / 30-day star delta computation | daily 06:00 UTC |
API
All endpoints under BASE_PATH. No auth.
GET /swarm-board/health→{ status: "ok" }GET /swarm-board/api/runners→ list with merged signals and deltas. Query params:kind,isolation,license,review_gate,sort.GET /swarm-board/api/runners/:slug→ full detail + 30-day sparklineGET /swarm-board/api/movers?window=7d|30d→ top-10 moversGET /swarm-board/api/stats→ aggregate countsPOST /swarm-board/api/refresh/:slug→ force a single-runner refresh (rate-limited 1/min)
Curated roster
data/runners.seed.json ships with 20 runners covering the parallel coding agent space: Microsoft Conductor, ryanmac/code-conductor, johannesjo/parallel-code, gemini-cli-extensions/conductor, Goose, Crush, Cline, Roo Code, Aider, OpenClaw, Sketch, Phasr, Conductor.build, AmpCode, Augment Intent, Tonkotsu, Squad, Cursor Background Agents, Devin, Factory Droid. Add new entries by appending to the JSON and restarting — the seed loader is upsert-safe.
No mocks, no fake data
Every signal you see comes from a real fetch. If a refresh fails the prior value is preserved and a "stale" badge appears after 24 hours. There are no hardcoded star counts, no Math.random() jitter, no preset fallback data. When we cannot parse a feature from text, we say unknown and surface the manual hint from the seed file.
License
MIT