lockfile-census
Live, public market-share dashboard for every package manager on GitHub. One screen. No auth. No mock data. Refreshed every six hours from the GitHub Code Search API.
lockfile-census answers the recurring engineering-Slack debate —
"should we move to pnpm? to bun? to uv? is yarn dead yet?" — with a count.
Not a survey, not an opinion piece, just the number of public GitHub repos
that contain each lockfile, snapshotted every 6 hours and plotted over time.
Why it exists
May 2026 has been a brutal month for package-manager politics:
- OpenAI is acquiring Astral (the makers of
uvandruff) to compete - with Anthropic's Claude Code stack. Python tooling consolidation is now an
- AI-lab fight, and every Python team is debating their lockfile choice.
- The Mini Shai-Hulud worm is still eating npm scopes (TanStack, UiPath,
- Mistral, Guardrails, …) via cache-poisoned GitHub Actions and abused OIDC
- trusted publishing, and RubyGems paused account signups on May 12, 2026
- after a "major malicious attack." Teams are mid-migration off compromised
- registries whether they like it or not.
- The only "objective" public numbers — GitHub Octoverse and the State of JS
- survey — are 6–12 months stale by the time anyone reads them.
lockfile-census is the alternative: a transparent, append-only counter
that anyone can audit.
What it shows
- KPI strip — total lockfiles tracked, most-used package manager globally,
- fastest grower over the last 7 days, and snapshot completeness.
- Ecosystem tabs — JS / Python / Rust / Go / Ruby / PHP / Elixir / JVM /
- .NET / Nix / Dart / Nim / Swift / All.
- Share donut — current share for the selected ecosystem.
- 30-day trend chart — one line per package manager.
- Leaderboard table — sortable by latest count, with 7d Δ and 30d Δ
- columns. Click any row to load the top 5 GitHub repos by stars that use
- that lockfile.
- Movers panel — top growers and top fallers (7d).
- Fetch log — collapsible drawer with every API call we made, with
- timestamp, HTTP status, duration, and error if any. Demonstrates that
- every number on the page came from a real GitHub Code Search response.
Data sources
All public. Refresh frequencies are honest — they match what node-cron
actually runs.
| Source | URL pattern | Frequency |
|---|---|---|
| GitHub Code Search API | https://api.github.com/search/code?q=filename:<lockfile>&per_page=1 | every 6 hours (0 /6 ) |
| GitHub Repos API | https://api.github.com/repos/<owner>/<name> | once daily, top-repo enrichment (0 2 *) |
Setting GITHUB_TOKEN is required — Code Search is auth-only for thetotal_count we depend on. Without it, every cell renders — and the
fetch log loudly says missing GITHUB_TOKEN. The dashboard never falls
back to fake counts.
Hard rules
- No mock data, no synthetic numbers, no
Math.random(), no seed fallback. - If GitHub returns an error or 422, the row is stored as
NULLand the - failure is in the fetch log. Audit the
snapshotandfetch_logtables — - every count traces back to a
raw_urlwe hit and thehttp_statuswe got. - No auth on any endpoint. Every route is public — read and write. There
- is no Basic Auth, no
ADMIN_PASS, no admin login. EvenPOST /refresh - is open. This is intentional: Arda wants to inspect it instantly.
Tracked lockfiles
| id | filename | ecosystem | display name |
|---|---|---|---|
| npm | package-lock.json | js | npm |
| pnpm | pnpm-lock.yaml | js | pnpm |
| yarn | yarn.lock | js | Yarn |
| bun | bun.lock | js | Bun (text) |
| bun_b | bun.lockb | js | Bun (binary) |
| deno | deno.lock | js | Deno |
| uv | uv.lock | py | uv |
| poetry | poetry.lock | py | Poetry |
| pipenv | Pipfile.lock | py | Pipenv |
| pdm | pdm.lock | py | PDM |
| pixi | pixi.lock | py | Pixi |
| conda | conda-lock.yml | py | conda-lock |
| cargo | Cargo.lock | rust | Cargo |
| go | go.sum | go | Go modules |
| gemfile | Gemfile.lock | ruby | Bundler |
| composer | composer.lock | php | Composer |
| mix | mix.lock | elixir | Mix |
| gradle | gradle.lockfile | jvm | Gradle |
| maven | pom.xml | jvm | Maven (proxy) |
| dotnet | packages.lock.json | dotnet | NuGet |
| flake | flake.lock | nix | Nix flakes |
| pubspec | pubspec.lock | dart | pub |
| sbt | build.sbt | jvm | sbt (proxy) |
| nimble | nimble.lock | nim | nimble |
| swift | Package.resolved | swift | Swift PM |
Running locally
npm install
cp .env.example .env
# put a real GitHub PAT in .env (any classic or fine-grained token works)
node server.js
# → listening on http://0.0.0.0:4761/lockfile-census/
The first snapshot fires about 5 seconds after boot so the dashboard is
populated immediately. Subsequent snapshots run on the cron schedule.
Endpoints
All public, all read-only except /refresh, which manually fires a
snapshot (still no auth).
| Method | Path | Notes |
|---|---|---|
| GET | /lockfile-census/ | Static SPA |
| GET | /lockfile-census/health | {status, snapshots, last_snapshot_at} |
| GET | /lockfile-census/api/lockfiles | Canonical list of trackable lockfiles |
| GET | /lockfile-census/api/latest | Latest non-null count per lockfile; ?ecosystem= filter |
| GET | /lockfile-census/api/history | ?lockfile=<id>&days=30 or all-series shape |
| GET | /lockfile-census/api/movers | Growers + fallers over the last 7 days |
| GET | /lockfile-census/api/share | ?ecosystem=py — share breakdown for that ecosystem |
| GET | /lockfile-census/api/top-repos | ?lockfile=<id> — top 5 repos by stars |
| GET | /lockfile-census/api/fetch-log | Last 100 fetch_log rows |
| POST | /lockfile-census/refresh | Manual snapshot kick (also accepts GET) |
JSON envelope: { "ok": true, "data": ..., "as_of": "ISO-8601" }.
Tech stack
- Node.js 20+, Express 4
better-sqlite3with WAL mode (DB lives atdata/lockfile-census.db)node-cron,helmet,compression- Vanilla JS SPA, dark theme, Chart.js from
cdn.jsdelivr.net - Native
fetch— nonode-fetchdep
File layout
lockfile-census/
├── server.js
├── db.js
├── package.json
├── .env.example
├── .gitignore
├── README.md
├── CLAUDE.md
├── SPEC.md
├── DEPLOY_MANIFEST.json
├── fetchers/
│ ├── github-code-search.js
│ └── github-top-repos.js
├── routes/
│ ├── api.js
│ └── refresh.js
├── data/ # created at runtime
└── public/
├── index.html
├── app.js
└── style.css
License
MIT.
Built by the Cowork R&D pipeline (Claude Opus 4.7).