Skip to content

Add Cloud Armor rate limiting for api.policyengine.org - #3756

Merged
anth-volk merged 6 commits into
masterfrom
feat/lb-cloud-armor
Jul 21, 2026
Merged

Add Cloud Armor rate limiting for api.policyengine.org#3756
anth-volk merged 6 commits into
masterfrom
feat/lb-cloud-armor

Conversation

@anth-volk

@anth-volk anth-volk commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes #3759
Fixes #3760

Summary

  • New repeated-use runbook docs/migration/lb-cloud-armor-runbook.md: Cloud Armor Standard policy pol-api-lb attached to both LB backend services (bs-app-engine, bs-cloud-run)
  • Two targeted per-IP throttle rules — /{cc}/metadata at 30/60s, /{cc}/calculate(-full) at 75/60s — created in preview mode; enforcement gated on ≥24h preview observation + 7-day empirical per-IP rate analysis
  • Deliberately no blanket/ban rules: app-v2 polls /economy/ and /report/ at ~1 req/s per client, and NAT'd offices aggregate users onto single IPs
  • Policy snapshots will be committed under docs/migration/armor/ as they change (mirrors the urlmap/ convention)

Also included: calculate malformed-payload 400 fix (#3760)

get_calculate (serving /calculate and /calculate-full) now catches SituationParsingError before the generic catch-all and returns 400 with the standard error shape; unexpected exceptions still 500. These payloads are overwhelmingly bot traffic (facebookexternalhit, 300–700/day) and were the dominant noise source in 5xx monitoring. Tests: 400 on both routes + control that real errors still 500. Folded in from #3757 — this PR's merge carries it through the deploy chain.

Context

Overnight 2026-07-21 bot waves (facebookexternalhit re-crawl + three scraper networks) churned Cloud Run scale-outs all night; each ~3-min import boot queued requests into monitor timeouts and 504s. This rate-limits the abuse surface at the edge for both backends. Cost ≈ $7.30/mo.

🤖 Generated with Claude Code

Two targeted per-IP throttle rules (metadata 30/60s, calculate 75/60s),
preview-first rollout with empirical enforcement gates. No blanket or ban
rules: the frontends poll /economy/ and /report/ at ~1 req/s, so only
never-polled path families are rate-limited.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.03%. Comparing base (344316f) to head (8c08b5f).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3756      +/-   ##
==========================================
+ Coverage   79.92%   80.03%   +0.11%     
==========================================
  Files          70       70              
  Lines        4338     4342       +4     
  Branches      808      809       +1     
==========================================
+ Hits         3467     3475       +8     
+ Misses        652      648       -4     
  Partials      219      219              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

anth-volk and others added 4 commits July 21, 2026 17:23
Policy live on both backend services in preview. Runbook expression
corrected: Cloud Armor regex rejects capture groups, (?:-full)? used.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Parameterized commands ($PROJECT/$POLICY/$BACKENDS placeholders); live
rule values live in the exported snapshots under docs/migration/armor/,
with only a compact current-deployment summary in the runbook.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Malformed household payloads (e.g. employment_income sent as a dict) are
client errors — mostly facebookexternalhit bot traffic, ~300-700/day —
but the catch-all in get_calculate turned them into HTTP 500s, polluting
error-rate monitoring. Catch SituationParsingError explicitly and return
400 with the standard error shape; genuine internal failures still 500.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@anth-volk
anth-volk merged commit 9f5b670 into master Jul 21, 2026
10 checks passed
@anth-volk
anth-volk deleted the feat/lb-cloud-armor branch July 21, 2026 19:38
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.

Malformed calculate payloads return 500 instead of 400 Bot traffic waves cause API outages; add rate limiting at the load balancer

1 participant