Problem. Every list handler parses limit and passes it straight to SQL with no maximum (handlers/packets.go:96-104, nodes.go:70-78, observers.go, channels.go, messages.go, routes.go, stats.go ×5, traces.go). ?limit=1000000 is accepted, so a scraper can pull a whole table in a handful of requests and per-request rate limiting never bounds egress. List endpoints also bypass the Redis reader (internal/cache/reader.go:381-448), so each such request lands on Postgres.
Ask.
- One
parseLimit(r, def, max) helper in handlers/, used by every list handler. Suggested max 200 (traces already asks 200; the SPA otherwise never asks above 100).
- Over-max: clamp or 400, either is fine, but log it with the client IP. Asking for more than the UI ever does is a strong scraper signal.
routes.go:55-59 silently ignores a parse error; make it 400 like the others.
Context. Part of the anti-scraping work (2026-09-02): edge access logs, header hygiene and block lists are live on both deployments; beacon-web dev already handles 429 + Retry-After.
Problem. Every list handler parses
limitand passes it straight to SQL with no maximum (handlers/packets.go:96-104,nodes.go:70-78,observers.go,channels.go,messages.go,routes.go,stats.go×5,traces.go).?limit=1000000is accepted, so a scraper can pull a whole table in a handful of requests and per-request rate limiting never bounds egress. List endpoints also bypass the Redis reader (internal/cache/reader.go:381-448), so each such request lands on Postgres.Ask.
parseLimit(r, def, max)helper inhandlers/, used by every list handler. Suggested max 200 (traces already asks 200; the SPA otherwise never asks above 100).routes.go:55-59silently ignores a parse error; make it 400 like the others.Context. Part of the anti-scraping work (2026-09-02): edge access logs, header hygiene and block lists are live on both deployments; beacon-web
devalready handles 429 +Retry-After.