Skip to content

fix(rate-limit): meter the gateway reverse-geocode RPC at 60/min - #6451

Merged
koala73 merged 2 commits into
mainfrom
fix/reverse-geocode-rpc-rate-limit-6432
Aug 11, 2026
Merged

koala73 merged 2 commits into
mainfrom
fix/reverse-geocode-rpc-rate-limit-6432

Conversation

@koala73

@koala73 koala73 commented Aug 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

GET /api/infrastructure/v1/reverse-geocode reaches Nominatim on a cache miss, but it had no ENDPOINT_RATE_POLICIES entry, so it silently inherited the gateway's 600/min availability-first fallback. That is 10x the 60/min budget #6412 set on the sibling api/reverse-geocode.js edge route - from the same provider, the same egress IPs, against a provider that bans egress IPs. Half the exposure #6412 closed was still open.

This closes it: the RPC is now registered at the same 60/min, and the edge route's cache no longer leaves ocean/Antarctic cells as unmetered passthrough.

What changed

  • Metered the RPC. '/api/infrastructure/v1/reverse-geocode': { limit: 60, window: '60 s' } in ENDPOINT_RATE_POLICIES (server/_shared/rate-limit.ts). The gateway already runs checkEndpointRateLimit for every route before consulting the registry, so the entry is the whole metering - no handler change needed. Because Nominatim enforces by egress-IP ban, it also goes into FAIL_CLOSED_ENDPOINT_RATE_POLICY_REQUIRED, so a Redis outage returns 503 rather than inheriting the fail-open fallback.
  • Cache-write parity (fix(api): rate-limit the three unguarded public proxies (#6234) #6412 follow-up). api/reverse-geocode.js wrote the shared geocode: cache only when a country resolved, so ocean and Antarctic coordinates were 100% Nominatim passthrough with no meter. The write is now unconditional and uses the RPC's complete entry shape {country, code, displayName, error}. Both routes use the same deployment-scoped 0.1-degree grid namespace, so either reader can serve the other without mixing preview and production entries.
  • Cache-read parity. The edge route normalizes legacy RPC cache values before returning them, so fresh and cached success responses have the same four fields.
  • Docs. The provider-proxies table in docs/usage-rate-limits.mdx and its Chinese translation gains the RPC row and corrects the now-false "none of them is routed through the gateway" claim.

Residual

Both budgets are per-IP, so they bound any single caller but not aggregate egress to Nominatim. A global companion budget is still required and is not implemented by this PR.

Verification

  • tests/rate-limit.test.mts: 50/50 pass, including the 60/min registry contract, fail-closed Redis behavior, and the real gateway rejection path.
  • tests/reverse-geocode-rate-limit.test.mjs: 6/6 pass, including preview-key interoperability, response normalization, and the complete seven-day negative-cache payload.
  • tests/reverse-geocode-cache-contract.test.mts: 2/2 pass for preview reads and writes across the shared cache contract.
  • tests/edge-functions.test.mjs: 252/252 pass.
  • typecheck:api, enforce-rate-limit-policies, Biome, and Markdown lint pass.

Fixes #6432

@mintlify

mintlify Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview Aug 11, 2026, 2:43 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@vercel

vercel Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview Aug 11, 2026 6:57am

Request Review

The gateway RPC server/worldmonitor/infrastructure/v1/reverse-geocode.ts
fetches Nominatim directly but had no ENDPOINT_RATE_POLICIES entry, so it
inherited the 600/min availability-first fallback - 10x the budget #6412
set on the sibling api/reverse-geocode.js edge route, against the same
provider from the same egress IPs.

- Register /api/infrastructure/v1/reverse-geocode at 60/min in
  ENDPOINT_RATE_POLICIES and FAIL_CLOSED_ENDPOINT_RATE_POLICY_REQUIRED
  (Nominatim enforces by egress-IP ban, so a Redis outage must 503 rather
  than inherit fail-open).
- Make api/reverse-geocode.js cache writes unconditional so ocean and
  Antarctic cells populate the shared geocode: namespace instead of being
  100% Nominatim passthrough, using the RPC's exact entry shape so either
  reader can serve the other.
- Update docs/usage-rate-limits.mdx (+ zh mirror) and add regression tests
  for both the fail-closed policy and the ocean-cell cache write.
@koala73
koala73 force-pushed the fix/reverse-geocode-rpc-rate-limit-6432 branch from 5413d38 to 0378ab9 Compare August 11, 2026 06:52
@koala73
koala73 merged commit 093c8a8 into main Aug 11, 2026
33 checks passed
@koala73
koala73 deleted the fix/reverse-geocode-rpc-rate-limit-6432 branch August 11, 2026 07:03

This branch was successfully deployed

2 active deployments
Preview — 0378ab9c Deployed Aug 11, 2026 by vercel[bot]
staging - docs — 0378ab9c Deployed Aug 11, 2026 by mintlify[bot]
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.

fix(rate-limit): the gateway reverse-geocode RPC still reaches Nominatim at 600/min, so half the egress exposure #6412 closed is still open

1 participant