Add tower-governor rate limits and trim public indexer status metrics #328
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#328
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Parent
GitLab #325 — Security review (findings F-07, F-08; approved recommendation #4).
Explicit scope from parent comment: implement in-process
tower-governorrate limiting. Do not rely on Cloudflare or nginx edge rate limits (rejected — monopoly concern).Current codebase
indexer/src/api.rs: router exposes/healthz(plainok),/v1/status(rich JSON), and many unauthenticatedGETroutes.clamp_limitallowslimit1–200 on paginated endpoints.indexer/src/api_arena.rs: expensive list routes (/v1/arena/buys,/v1/arena/activity,/v1/arena/platform-usage, etc.) with no per-IP throttling.indexer/src/main.rs: stackstower_httpCORS + trace; no rate-limit layer today.indexer/Cargo.toml:tower-httpwithcors,traceonly; notower-governordependency yet./v1/statusreturnsrpc_metrics(call rates, method/caller breakdown),chain_pointer,max_indexed_block,ingestion_alive,last_indexed_at_ms— useful for ops reconnaissance (F-08).indexer/README.mddocumentsINV-INDEXER-306-STATUS-METRICSforrpc_metricson/v1/status./healthzand/v1/statusinindexer/tests/integration_stage2.rs.Why needed
Public read API has no rate limiting. Attackers can script pagination (
limit=200) across large tables to pressure DB CPU/memory and degrade availability. Detailed/v1/statusaids timing/recon for ingestion lag and RPC load patterns. Parent #325 approved in-process rate limits and tighter status exposure.Constraints / guardrails
/healthzminimal for load balancers.frontend/src/lib/indexerApi.tspolling (/v1/arena/timers,/v1/statusor health checks) under normal traffic — tune limits above legitimate UI refresh rates.x-schema-versionheader behavior on JSON routes.INDEXER_RATE_LIMIT_PER_MIN) with sensible defaults.Relevant files
indexer/src/api.rs,indexer/src/api_arena.rs,indexer/src/main.rsindexer/src/rpc_metrics.rsindexer/Cargo.tomlindexer/tests/integration_stage2.rs,scripts/verify-indexer-rpc-metrics.shfrontend/src/lib/indexerApi.ts, arena hooksindexer/README.md,docs/indexer/design.md,docs/testing/invariants-and-business-logic.mdRecommended direction
Rate limiting (F-07)
tower-governor(ortower_governor+governorcrates compatible with axum 0.8).ConnectInfoorX-Forwarded-Forwhen trusted) limits on publicGETroutes; stricter bucket for heavy list endpoints if needed.429 Too Many Requestswith generic JSON body (no stack traces)./healthzor give it a generous bucket.127.0.0.1.Status split (F-08)
GET /healthzas liveness only (ok+ optionaldatabase_connectedif cheap).GET /v1/statusfor public callers or addGET /v1/status/ops(or similar) for detailed metrics behind env flag / optional bearer token / internal bind only./v1/status:schema_version,database_connected,max_indexed_block,ingestion_alive— drop or gaterpc_metricsand detailedchain_pointerfor production unlessINDEXER_EXPOSE_OPS_METRICS=1.INV-INDEXER-306docs and frontend if it depends on removed fields (checkindexerApi.ts).Acceptance criteria
/v1/statusno longer exposes fullrpc_metricsby default in production mode (or gated)./healthzsuitable for LB probes unchanged or improved.cd indexer && cargo clippy --all-targets -- -D warnings && cargo testpasses.Test plan
cd indexer && cargo clippy --all-targets -- -D warnings && cargo testcd indexer && cargo test --test integration_stage2bash scripts/verify-indexer-rpc-metrics.sh(update if status path changes)bash scripts/verify-podium-live-anvil.shorbash scripts/verify-wallet-profile-anvil.shcd frontend && npm run typecheck && npm test(if indexer client types change)curlloop to list endpoint → expect 429 after thresholdAttack / abuse test plan
GET /v1/arena/buys?limit=200— observe 429 and indexer process stability.curl /v1/statuson production-config indexer:rpc_metricsabsent or empty unless ops flag set.INV-FRONTEND-301).Verification criteria
mentioned in issue #325
mentioned in merge request !502
mentioned in commit
35cecc13feVerification — GitLab #328 (PASS)
Verified on
main@35cecc13(merged !502). No repo changes required.Acceptance criteria
bash scripts/verify-indexer-rate-limit.sh→ok=3 throttled=5 healthz=200; unit testsrate_limit::excess_requests_return_429bash scripts/verify-podium-live-anvil.sh→ integration_stage2 4/4;cd frontend && npm run typecheck && npm test→ 676/676 (includesindexerApi429 backoff +indexerFirstDisplay)/v1/statusomitsrpc_metricsby defaultbash scripts/verify-indexer-rpc-metrics.sh; unit testpublic_status_omits_rpc_metrics_by_default/healthzsuitable for LB probesGET /healthz→ 200 while/v1/arena/buysthrottles to 429cargo clippy+cargo testcd indexer && cargo clippy --all-targets -- -D warnings && cargo test→ 74 unit + 4 integrationstatus_exposure_tests,rate_limitunit tests;cargo test --test integration_stage2(included in full suite)Test plan (issue body)
cd indexer && cargo clippy --all-targets -- -D warnings && cargo testcd indexer && cargo test --test integration_stage2bash scripts/verify-indexer-rpc-metrics.shbash scripts/verify-podium-live-anvil.shcd frontend && npm run typecheck && npm testcurl→ 429 on list endpointverify-indexer-rate-limit.sh)Attack / abuse checks
GET /v1/arena/buys?limit=200from one IP → 429, process stablecurl /v1/statuswithout ops flag → norpc_metricsINV-FRONTEND-301)Constraints / parent #325
INDEXER_RATE_LIMIT_PER_MIN(default 600),INDEXER_RATE_LIMIT_BURST(120),INDEXER_TRUST_PROXY,INDEXER_EXPOSE_OPS_METRICS—indexer/README.md,indexer/.env.example, invariantsINV-INDEXER-328-*GET /v1/status/opswhenINDEXER_EXPOSE_OPS_METRICS=1(PASS)x-schema-versionpreserved on JSON routes (PASS, smoke scripts)Infra
127.0.0.1:5433—bash scripts/verify-cloud-postgres.shPASSmentioned in issue #341