Rate limits

The two independent limits on the ingest endpoint, what happens when one trips, and how to think about the defaults.

Two windows, both enforced

Every submission consumes budget in two separate fixed windows: one for the project as a whole, and one for the individual submitter, identified by the salted hash of their IP. A submission is accepted only if both have room.

The per-submitter window is what stops one person hammering the form. The per-project window is what stops a distributed flood from filling your issue tracker.

Defaults

Out of the box: 120 submissions per project per hour, and 8 per submitter every 10 minutes. Both are configured per deployment rather than per project, so a self-hosted instance can tune them for its own traffic.

What a visitor sees

A 429, rendered by the widget as "Too many reports from here just now. Try again in a few minutes." Nothing is lost — they can resubmit and their text is still in the form. The screenshot endpoint answers the same way and adds Retry-After: 60; the feedback endpoint sends no Retry-After, because the project window is an hour and a number would be guesswork.

Design note

The counters live in Postgres rather than a separate cache. That is one fewer service to run, one fewer thing to page someone about, and the accounting is transactional. If the counter query itself fails, the request is refused with a 503 and the failure is logged at error level — this surface is unauthenticated and writes to storage, so a limiter that cannot answer must not become an open door. A 503 also tells the reporter the truth: nobody is over quota, and it is not their fault.