fix(e2e): back off on a 429 from the register-challenge rate floor #440
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-449-e2e-register-challenge-429-backoff"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
BUNYIP-449 (relates BUNYIP-445). Second of the split fixes from the e2e.yml failure triage.
Problem
registerDisposable(e2e/lib/accounts.ts) fetchedGET /v1/auth/register-challengeand threw on the first non-ok status. On BUNYIP-445 run #2453 that GET returned429on the first hit (~510ms, empty body), failingmagic-link.spec.tsandpassword-reset.spec.tsoutright.The 429 is not a stale-image artifact. On current main,
register-challengeis not inrate_limit_floor::EXEMPT_PATHS, so it sits under the per-IPAPI_UNAUTHfloor (RateLimitConfig::API_UNAUTH= 20 req / 60s / IP,crates/bunyip-domain/src/models/rate_limit.rs:115). The E2E runner has a single egress IP that shares that bucket, and the failing pushes came in bursts (#2453/#2457/#2463 within an hour), so the aggregate of unauthenticated requests across overlapping runs exceeds 20/min and the nextregister-challenge429s. The handler itself sets no limit ("minting a signed timestamp is harmless",bunyip-api/src/handlers/auth.rs:387), but the floor still applies.Change
Wrap the challenge GET in a bounded retry (
fetchRegisterChallenge) that fires only on 429:Retry-Afterheader (rate_limit_floor.rs:139-140sets it to the seconds until the window frees) up to a 65s ceiling, so one wait clears the 60s window.Retry-After, and body snippet.429.Any non-429 non-ok status still surfaces immediately (unchanged). The register POST and the bot-guard timing wait are untouched.
Scope / follow-up
This is the harness-side resilience. The durable root-cause fix is deployment/product-side and intentionally out of scope here: either exempt
/v1/auth/register-challengefrom the floor (which matches the handler's documented "needs no rate limit" intent) or raiseRATE_LIMIT_API_UNAUTH_MAX_REQUESTSfor staging. That is a security-reviewed change to file separately.Verification
node_modules/.bin/tsc --noEmit(e2e, strict) passes.🤖 Generated with Claude Code