Skip to content

feat: harden cli against github api rate limits#122

Merged
UtkarshBhardwaj007 merged 2 commits into
mainfrom
feat/gh-rate-limits
May 5, 2026
Merged

feat: harden cli against github api rate limits#122
UtkarshBhardwaj007 merged 2 commits into
mainfrom
feat/gh-rate-limits

Conversation

@UtkarshBhardwaj007

Copy link
Copy Markdown
Member

Summary

Defends dot against GitHub's 60/hour anonymous-IP rate limit, which gets exhausted quickly on shared networks (hackathon WiFi, conferences, corporate NATs). Four layered fixes:

  1. Version-check banner via jsDelivr. Every dot invocation prints a yellow ⚠ "Update available" banner at the bottom when a newer release exists. The latest-version lookup goes through jsDelivr's free public CDN, not GitHub — so the check itself contributes zero load to the rate-limited quota. 1 s AbortSignal.timeout so a flaky network never delays exit. Suppressed in CI / non-TTY / piped output, for dot update/help/--version/--help, and when DOT_NO_UPDATE_CHECK=1.
  2. Opportunistic gh auth token. assertPublicGitHubRepo, resolveDefaultBranch, and fetchLatestTag now add an Authorization: Bearer <token> header read from gh auth token when available — a gh-authed user lands on their personal 5000/hour quota instead of the shared anonymous bucket. Anonymous users continue to work as before.
  3. Fail-closed rate-limit handling in assertPublicGitHubRepo. A 403 with x-ratelimit-remaining: 0 now raises an actionable ModablePreflightError recommending gh auth login, instead of silently passing the public-repo check (which previously risked publishing a private repo as --modable). Ambiguous 403s and 5xx transient errors still fall through.
  4. End-of-init advisory banner. dot init ends with a Callout-style rounded yellow box (mirroring the in-Ink "check your phone" deploy callout) that explains the IP-based rate limit and recommends gh auth login. Only shown when the user is not currently authed.

Audit — every api.github.com/* call site uses opportunistic auth

Site Endpoint Auth
src/commands/update.ts:46 releases/latest ghAuthHeaders()
src/utils/deploy/modable.ts:112 repos/{owner}/{repo} ghAuthHeaders()
src/utils/mod/source.ts:45 repos/{owner}/{repo} ghAuthHeaders()

Non-API GitHub URLs (separate, much higher rate limits — don't share the API quota) are left as-is: github.com/.../tree/{branch} HTML probe, codeload.github.com/.../tar.gz/... tarball, github.com/.../releases/download/... asset.

Banner previews

Version-check (every command, footer):
```
⚠ Update available: v0.16.14 → v0.17.0
Run dot update to upgrade.
```

End-of-init (only when not gh-authed):
```
╭──────────────────────────────────────────────────────────────────────╮
│ GitHub authentication recommended │
│ │
│ Without gh auth login, GitHub rate-limits all requests from your │
│ public IP to 60/hour, shared with everyone on the same network. │
│ This will exhaust quickly on public wifis. Run gh auth login once │
│ to use your personal 5000/hour quota instead. │
╰──────────────────────────────────────────────────────────────────────╯
```

Test plan

  • `pnpm vitest run` — 501/501 pass (added 30 new across version-check / gh-token / gh-auth-banner, expanded modable)
  • `pnpm exec tsc --noEmit` — clean
  • `pnpm format:check` — clean
  • Visual smoke: faked an old version, confirmed banner appears at bottom of `dot logout`
  • Visual smoke: rendered `formatGhAuthBanner()` directly to verify Callout-style box
  • Local install (`pnpm cli:install`) and run `dot init` while logged out → confirm advisory banner; while logged in → confirm no banner
  • Local install and run a slow command on offline network → confirm no exit delay (1 s timeout)

Notes

  • No on-disk cache for the version check; jsDelivr's effectively-unlimited rate limits make caching unnecessary.
  • The two banners use deliberately different visual weights — version-check is plain (fires every command, must stay quiet); init advisory is a box (fires once per machine, deserves emphasis). Comment in formatBanner warns against "harmonising" them.
  • dot init's existing dependency-list row for gh auth status is kept as-is; the new bottom banner only adds the why.

Defends `dot` against GitHub's 60/hour anonymous-IP rate limit, which
gets exhausted quickly on shared networks (hackathon WiFi, conferences,
corporate NATs). Four parts:

1. Version-check banner via jsDelivr. Every `dot` invocation prints a
   yellow ⚠ "Update available" banner at the bottom when a newer
   release exists. Resolves the latest version through jsDelivr's free
   public CDN (NOT GitHub's API), so the check itself contributes zero
   load to the rate-limited quota. 1 s `AbortSignal.timeout` so a flaky
   network never delays exit. Suppressed in CI / non-TTY / piped output,
   for `dot update`/`help`/`--version`/`--help`, and when
   `DOT_NO_UPDATE_CHECK=1`.

2. Opportunistic `gh auth token` injection. `assertPublicGitHubRepo`,
   `resolveDefaultBranch`, and `fetchLatestTag` now add an
   `Authorization: Bearer <token>` header read from `gh auth token`
   when available — a `gh`-authed user lands on their personal
   5000/hour quota instead of the shared anonymous bucket. Anonymous
   users continue to work as before. Token is cached for the process
   lifetime.

3. Fail-closed rate-limit handling in `assertPublicGitHubRepo`. A 403
   with `x-ratelimit-remaining: 0` now raises an actionable
   `ModablePreflightError` recommending `gh auth login`, instead of
   silently passing the public-repo check (which previously risked
   publishing a private repo as `--modable`). Ambiguous 403s and 5xx
   transient errors still fall through.

4. End-of-init advisory banner. `dot init` ends with a Callout-style
   rounded yellow box (visually mirroring the in-Ink "check your
   phone" deploy callout) that explains the IP-based rate limit and
   recommends `gh auth login`. Only shown when the user is not
   currently authed.
@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Dev build ready — try this branch:

curl -fsSL https://raw.githubusercontent.com/paritytech/playground-cli/main/install.sh | VERSION=dev/feat/gh-rate-limits bash

@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

E2E Test Pass · ✅ PASS

Tag: e2e-ci-pr · Branch: feat/gh-rate-limits · Commit: 71cc607 · Run logs

Cell Result Time
pr-preflight ✅ PASS 2m07s
pr-install ✅ PASS 0m49s
pr-mod ✅ PASS 1m26s
pr-deploy-foundry ✅ PASS 1m33s
pr-init-session ✅ PASS 1m35s
pr-deploy-frontend ✅ PASS 10m05s
${{ matrix.cell }} ⏭️ SKIP 0m-1s
${{ matrix.cell }} ⏭️ SKIP 0m-1s

Sentry traces: view spans for this run

@UtkarshBhardwaj007 UtkarshBhardwaj007 merged commit d6580e2 into main May 5, 2026
17 checks passed
@UtkarshBhardwaj007 UtkarshBhardwaj007 deleted the feat/gh-rate-limits branch May 5, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant