Skip to content

site: never show "no release" because GitHub rate-limited us - #6

Merged
ralyodio merged 1 commit into
mainfrom
site/release-lookup-resilience
Aug 30, 2026
Merged

site: never show "no release" because GitHub rate-limited us#6
ralyodio merged 1 commit into
mainfrom
site/release-lookup-resilience

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

diskpush.com/download showed no release at all while v0.1.9 was published, built and downloadable. /api/releases/latest answered {"version":null,...} on every request.

What happened

Unauthenticated GitHub allows exactly 60 requests an hour per IP, and this is a shared host, so that budget is not ours alone.

Dropping the release cache from an hour to a minute (#1, 1798a06) made 60/hour the ceiling rather than a limit we never approached. Then fifteen AEO audit engines plus a recursive link checker crawled the site and spent it. Every call came back 403, latestRelease() returned null, and the page rendered as though the project had never shipped.

I flagged this risk when making that change; this is it happening. Confirmed live:

$ curl -sI https://diskpush.com/api/releases/latest | grep cache-control
cache-control: public, s-maxage=60          # the short-cache build is deployed

$ curl -s https://diskpush.com/api/releases/latest
{"version":null,"publishedAt":null,"assets":{...all null}}

$ curl -s https://api.github.com/rate_limit  # from elsewhere: GitHub is fine
"core": { "limit": 60, "remaining": 60 }

The fix

Rate limiting is a fact about our IP. It is not a fact about the project, and the site must not present it as one.

  • latestRelease() keeps the last release it read and returns that when a fetch is refused, fails, or throws. A version a few minutes old is a far better answer than no version. It still returns null before it has ever read one — that is the real pre-launch state the page has a rendering for.
  • GITHUB_TOKEN, when set, lifts the ceiling to 5,000/hour. Optional on purpose: the site has to work without one, which is what the fallback is for.

The shorter cache stays. It is why a release now appears within a minute, and with a fallback behind it a spent budget is no longer a failure.

Follow-up for you

Set GITHUB_TOKEN on the diskpush-web Railway service (a bare public-repo-read token is enough) and the fallback stops being load-bearing. Not required for this PR to fix the outage.

Tests

5 new tests in apps/web/lib/releases.test.ts. The three that pin the fix fail without it; the two describing unchanged behaviour — asset mapping, and null before the first read — pass either way.

Full suite 243 passing, typecheck clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y5jnkZKX4AdPgBMzMosxE7

diskpush.com/download showed no release at all while v0.1.9 was published,
built and downloadable. /api/releases/latest answered
{"version":null,...} on every request.

Unauthenticated GitHub allows exactly 60 requests an hour per IP, and this is
a shared host, so that budget is not ours alone. Dropping the release cache
from an hour to a minute (1798a06) made 60/hour the ceiling rather than a
limit we never approached — and then fifteen AEO audit engines plus a
recursive link checker crawled the site and spent it. Every call came back
403, latestRelease returned null, and the page rendered as though the project
had never shipped.

Rate limiting is a fact about our IP. It is not a fact about the project, and
the site must not present it as one.

  - latestRelease keeps the last release it read and returns that when a fetch
    is refused, fails, or throws. A version a few minutes old is a far better
    answer than no version. It still returns null before it has ever read one,
    which is the real pre-launch state the page has a rendering for.

  - GITHUB_TOKEN, when set, lifts the ceiling from 60/hour to 5,000. Optional
    on purpose: the site has to work without one, which is what the fallback
    is for. Set it on the service to stop relying on the fallback at all.

The shorter cache stays. It is the reason a release now appears within a
minute, and with a fallback behind it the spent budget is no longer a failure.

The three tests that pin this fail without the change; the two describing
unchanged behaviour — asset mapping, and null before the first read — pass
either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y5jnkZKX4AdPgBMzMosxE7
@ralyodio
ralyodio merged commit b343bca into main Aug 30, 2026
4 checks passed
@ralyodio
ralyodio deleted the site/release-lookup-resilience branch August 30, 2026 05:18
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