feat: harden cli against github api rate limits#122
Merged
Conversation
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.
Contributor
|
Dev build ready — try this branch: |
Contributor
E2E Test Pass · ✅ PASSTag:
Sentry traces: view spans for this run |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Defends
dotagainst GitHub's 60/hour anonymous-IP rate limit, which gets exhausted quickly on shared networks (hackathon WiFi, conferences, corporate NATs). Four layered fixes:dotinvocation 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 sAbortSignal.timeoutso a flaky network never delays exit. Suppressed in CI / non-TTY / piped output, fordot update/help/--version/--help, and whenDOT_NO_UPDATE_CHECK=1.gh auth token.assertPublicGitHubRepo,resolveDefaultBranch, andfetchLatestTagnow add anAuthorization: Bearer <token>header read fromgh auth tokenwhen available — agh-authed user lands on their personal 5000/hour quota instead of the shared anonymous bucket. Anonymous users continue to work as before.assertPublicGitHubRepo. A 403 withx-ratelimit-remaining: 0now raises an actionableModablePreflightErrorrecommendinggh 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.dot initends with a Callout-style rounded yellow box (mirroring the in-Ink "check your phone" deploy callout) that explains the IP-based rate limit and recommendsgh auth login. Only shown when the user is not currently authed.Audit — every
api.github.com/*call site uses opportunistic authsrc/commands/update.ts:46releases/latestghAuthHeaders()src/utils/deploy/modable.ts:112repos/{owner}/{repo}ghAuthHeaders()src/utils/mod/source.ts:45repos/{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
Notes
formatBannerwarns against "harmonising" them.dot init's existing dependency-list row forgh auth statusis kept as-is; the new bottom banner only adds the why.