Skip to content

Meter every route, and sell a pass to whoever goes over - #215

Merged
ralyodio merged 3 commits into
masterfrom
feat/throttle
Sep 9, 2026
Merged

Meter every route, and sell a pass to whoever goes over#215
ralyodio merged 3 commits into
masterfrom
feat/throttle

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Why

The limiter here watched three API prefixes and two page paths. That is the shape every site in the fleet had, and it is the shape that failed on coinpayportal on 2026-09-08.

A headless browser found /explorer fifteen hours after it shipped and walked 19,000 of its URLs a day for two days — 97% of all traffic to that site. It declared nothing, so no crawler list matched it. It never touched a listed path, so no limiter saw it.

The expensive routes were never the ones at risk. The unlisted ones were.

What changes

The tuned numbers stay:

Path Limit
/api/search/, /api/dht/, /api/torrent-search 30/min
/search, /dht 60/min
/api/auth/ 10/min, address-bucketed
everything else 100/min ← new

Everything not in that list is now metered too, via @profullstack/throttle. Going over is answered 402 with the gate's own x402 offer rather than 429, so a scraper that declares nothing is sold the same pass GPTBot buys.

What is untouched

  • The good-bot and bad-bot tiers still run first and stay tighter. This is the floor under them, not a replacement.
  • The denyCidrs OVH fleet block and chargeSpoofedBrowsers are unchanged.
  • A signed-in reader and an API bearer get the 600/min credentialed budget, keyed on the credential itself so two never share a bucket. The gate exempts both — it is deciding whether to charge a crawler, and a session is good evidence of a person. The throttle still meters them, because it is deciding whether anyone at all reads 19,000 pages an hour.

Verified

vitest run2788 passed, 9 skipped, across 208 files. Three new tests: an unlisted route (/torrent/abc123) is metered at 100, /api/torrent-search stays at 30, and callers get separate allowances. tsc --noEmit clean, next build clean with the proxy intact.

Note

The 402 only sells once COINPAY_X402_KEY and CRAWL_PAY_TO are set — the gateway already reads them here. Without them the throttle falls back to 429, which is the honest answer when there is nothing to buy.

🤖 Generated with Claude Code

ralyodio and others added 3 commits September 9, 2026 16:40
The limiter here watched three API prefixes and two page paths. That is the
shape every site in the fleet had, and it is the shape that failed on
coinpayportal on 2026-09-08: a headless browser found /explorer fifteen hours
after it shipped and walked 19,000 of its URLs a day for two days. It
declared nothing, so no crawler list matched it. It never touched a listed
path, so no limiter saw it. The expensive routes were never the ones at
risk -- the unlisted ones were.

So the tuned numbers stay (30/min on /api/search/, /api/dht/ and
/api/torrent-search, 60/min on /search and /dht) and everything NOT in that
list is now metered too, at 100/min per caller, via @profullstack/throttle.
Going over is answered 402 with the gate's own offer rather than 429, so a
scraper that declares nothing is sold the same pass GPTBot buys.

The good-bot and bad-bot tiers are untouched and still run first; this is the
floor under them, not a replacement. A signed-in reader and an API bearer get
the 600/min credentialed budget keyed on the credential itself -- the gate
exempts both, because it is deciding whether to charge a crawler and a
session is good evidence of a person, but this is deciding whether anyone at
all reads 19,000 pages an hour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDGCxTmEPs3ecwjjLJDQXh
Keeps this on the same two versions as the rest of the rollout, and drops the
second nested copy of the gateway: a caret range on a 0.x version only matches
patches, so the throttle's old ^0.5.0 floor could not resolve 0.6.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDGCxTmEPs3ecwjjLJDQXh
The only conflict was pnpm-lock.yaml, where master's dependency bumps
(next 16.3.4, @types/node 26.4.1) landed on the same importer lines that
this branch added @profullstack/throttle to. Taking either side alone
drops an entry, so both were kept and the lockfile re-resolved against
the merged package.json.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0139ts1a6Lzj5DXySTCbeG8C
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

99 finding(s)

HIGH/CRITICAL: 11 | MEDIUM: 29 | LOW: 59

Severity Rule Location
HIGH secret-private-key src/app/settings/seedbox-section.tsx:412
HIGH secret-generic-api-key docs/incidents/2026-05-okshanaby-supply-chain.md:18
HIGH tls-verification-disabled src/app/api/iptv-proxy/route.ts:38
HIGH tls-verification-disabled src/app/api/iptv/channels/route.ts:35
HIGH tls-verification-disabled src/app/api/iptv/playlists/[id]/route.ts:73
HIGH tls-verification-disabled src/app/api/iptv/playlists/route.ts:64
HIGH js-cors-origin-reflected src/app/api/public/shares/[slug]/checkout/route.ts:40
HIGH js-cors-origin-reflected src/app/api/public/vod/[slug]/checkout/route.ts:34
HIGH tls-verification-disabled src/lib/iptv/shares/upstream.ts:37
HIGH tls-verification-disabled workers/iptv-cache/epg-fetcher.ts:25
HIGH tls-verification-disabled workers/iptv-cache/playlist-fetcher.ts:62
MEDIUM secret-jwt .github/workflows/ci.yml:120
MEDIUM secret-jwt .github/workflows/ci.yml:121
MEDIUM secret-jwt .github/workflows/ci.yml:123
MEDIUM secret-jwt .github/workflows/ci.yml:162
MEDIUM secret-jwt .github/workflows/ci.yml:164
MEDIUM secret-jwt docs/tunein (2).py:9
MEDIUM secret-jwt docs/tunein.py:9
MEDIUM sh-remote-script-execution scripts/setup-server.sh:182
MEDIUM sh-remote-script-execution scripts/setup-server.sh:419
MEDIUM sh-remote-script-execution scripts/setup-server.sh:428
MEDIUM sh-unquoted-expansion-destructive scripts/setup-server.sh:1073
MEDIUM sh-unquoted-expansion-destructive scripts/setup-server.sh:1083
MEDIUM js-unescaped-html-sink src/app/api/player/route.ts:110
MEDIUM js-unescaped-html-sink src/app/api/player/route.ts:249
MEDIUM js-unescaped-html-sink src/app/blog/[slug]/page.tsx:40
MEDIUM js-unescaped-html-sink src/app/blog/[slug]/page.tsx:66
MEDIUM js-unescaped-html-sink src/app/email/email-content.tsx:566
MEDIUM js-open-redirect src/app/login/page.tsx:50
MEDIUM js-open-redirect src/app/pricing/page.tsx:162
MEDIUM js-open-redirect src/app/rent/[slug]/rent-client.tsx:170
MEDIUM js-unescaped-html-sink src/app/rss/rss-content.tsx:615
MEDIUM js-open-redirect src/app/vod/[slug]/vod-client.tsx:134
MEDIUM js-open-redirect src/app/watch/[slug]/watch-client.tsx:129
MEDIUM js-unescaped-html-sink src/app/youtube/youtube-content.tsx:546
MEDIUM js-open-redirect src/components/account/iptv-subscription-section.tsx:135
MEDIUM js-open-redirect src/components/account/iptv-subscription-section.tsx:167
MEDIUM js-unescaped-html-sink src/components/news/news-section.tsx:361
MEDIUM js-unescaped-html-sink src/components/news/news-section.tsx:734
MEDIUM redos-nested-quantifier src/lib/metadata-enrichment/metadata-enrichment.ts:317
LOW tls-verification-disabled docs/tunein (2).py:34
LOW tls-verification-disabled docs/tunein (2).py:37
LOW py-xpath-injection docs/tunein (2).py:37
LOW tls-verification-disabled docs/tunein (2).py:47
LOW py-xpath-injection docs/tunein (2).py:47
LOW tls-verification-disabled docs/tunein.py:34
LOW tls-verification-disabled docs/tunein.py:37
LOW py-xpath-injection docs/tunein.py:37
LOW tls-verification-disabled docs/tunein.py:47
LOW py-xpath-injection docs/tunein.py:47

…and 49 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​profullstack/​throttle@​0.2.27410010091100
Updated@​profullstack/​x402-gateway@​0.3.0 ⏵ 0.6.076 +1100100 +194 +2100

View full report

@ralyodio
ralyodio merged commit c8c19b8 into master Sep 9, 2026
9 checks passed
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