SWWAF_RATE_LIMIT_PER_MINUTE now defaults to 500, SWWAF_RATE_LIMIT_PER_HOUR to 20000 and SWWAF_RATE_LIMIT_PER_DAY to 300000. Byte limits and ban lengths are unchanged. The tests of the defaults follow.
"Request rate limits, per client" in SPEC.md, the README's "What it protects against" and docs/settings.md give each default, its reason, and what counting cannot catch.
docs/gitea.md: two Actions runners busy all day on one address pass the day limit; five busy at once pass the minute limit.
docs/alerts.md: the ban example is a crawler at 10 a second, banned by the minute limit at its 501st request, 50 seconds in.
What a reader might trip over:
SPEC.md's "Static lists" and "Byte limits, per client" changed too, since they relied on the old "40 busy people" sizing.
The documents give rates, not timings, apart from "within seconds" for the fastest clients. With two fixed buckets per window ("Counting method" in SPEC.md), a client starting fresh can take longer than one window to pass a limit.
Disclosures:
Judgement call: the issue says the day limit catches three or four requests a second. Three a second is 259,200 a day, under it, so the documents say four.
Unverified: the 40 files a Gitea page loads with an empty cache is the proposal's figure, not measured here.
Model: opus-5-5
Implements https://git.eeqj.de/sneak/smallwebwaf/issues/176, with the numbers at https://git.eeqj.de/sneak/smallwebwaf/issues/169#issuecomment-167815.
- `SWWAF_RATE_LIMIT_PER_MINUTE` now defaults to `500`, `SWWAF_RATE_LIMIT_PER_HOUR` to `20000` and `SWWAF_RATE_LIMIT_PER_DAY` to `300000`. Byte limits and ban lengths are unchanged. The tests of the defaults follow.
- "Request rate limits, per client" in `SPEC.md`, the README's "What it protects against" and `docs/settings.md` give each default, its reason, and what counting cannot catch.
- `docs/gitea.md`: two Actions runners busy all day on one address pass the day limit; five busy at once pass the minute limit.
- `docs/alerts.md`: the ban example is a crawler at 10 a second, banned by the minute limit at its 501st request, 50 seconds in.
What a reader might trip over:
- `SPEC.md`'s "Static lists" and "Byte limits, per client" changed too, since they relied on the old "40 busy people" sizing.
- The documents give rates, not timings, apart from "within seconds" for the fastest clients. With two fixed buckets per window ("Counting method" in `SPEC.md`), a client starting fresh can take longer than one window to pass a limit.
Disclosures:
- Judgement call: the issue says the day limit catches three or four requests a second. Three a second is 259,200 a day, under it, so the documents say four.
- Unverified: the 40 files a Gitea page loads with an empty cache is the proposal's figure, not measured here.
Model: opus-5-5
README.md line 24: "Counting cannot catch a slower client" follows "4 a second", so it says a client below 4 a second gets through. The day limit also catches anything over about 3.5 a second: 3.6 a second makes 311,040 a day. Acceptable: "a client slower than about 3.5 requests a second", matching SPEC.md and docs/settings.md ("slower than these limits").
docs/alerts.md lines 38-65: the example ban comes 0.123 seconds into the hour, with count 30001 and requests 30001. The hour bucket starts on the hour, and requests counts every request since the client was first seen. So the earlier bucket was empty, and all 30,001 requests came in those 0.123 seconds. Acceptable: times that fit a crawler's pace, such as a request at 12:50:00 for 10 a second since 12:00, with time and ban_expires moved to match.
SPEC.md line 18, in "Purpose": "It limits each client's requests and bytes per minute, hour and day." With no settings, requests are no longer limited per minute. Acceptable: "It limits each client's requests per hour and day, and its bytes per minute, hour and day."
SPEC.md line 395: "A busier machine that talks to the app all day can still pass the day limit." "Busier" has no stated comparison, and right after "an abuser that slow" it reads as busier than that abuser. Acceptable: name the comparison, such as "A machine busier than that fleet, such as two Actions runners busy all day, passes the day limit."
Judgement call: the PR body, slightly over 250 words, is taken as within the limit.
Model: opus-5-5
Review failed (`needs-rework`):
1. `README.md` line 24: "Counting cannot catch a slower client" follows "4 a second", so it says a client below 4 a second gets through. The day limit also catches anything over about 3.5 a second: 3.6 a second makes 311,040 a day. Acceptable: "a client slower than about 3.5 requests a second", matching `SPEC.md` and `docs/settings.md` ("slower than these limits").
2. `docs/alerts.md` lines 38-65: the example ban comes 0.123 seconds into the hour, with `count` 30001 and `requests` 30001. The hour bucket starts on the hour, and `requests` counts every request since the client was first seen. So the earlier bucket was empty, and all 30,001 requests came in those 0.123 seconds. Acceptable: times that fit a crawler's pace, such as a request at 12:50:00 for 10 a second since 12:00, with `time` and `ban_expires` moved to match.
3. `SPEC.md` line 18, in "Purpose": "It limits each client's requests and bytes per minute, hour and day." With no settings, requests are no longer limited per minute. Acceptable: "It limits each client's requests per hour and day, and its bytes per minute, hour and day."
4. `SPEC.md` line 395: "A busier machine that talks to the app all day can still pass the day limit." "Busier" has no stated comparison, and right after "an abuser that slow" it reads as busier than that abuser. Acceptable: name the comparison, such as "A machine busier than that fleet, such as two Actions runners busy all day, passes the day limit."
Judgement call: the PR body, slightly over 250 words, is taken as within the limit.
Model: opus-5-5
README.md: now says counting cannot catch "a client slower than about 3.5 requests a second".
docs/alerts.md: the example ban is now at 12:50:00, for a crawler at 10 a second since 12:00. time and ban_expires moved to match.
SPEC.md "Purpose": now "It limits each client's requests per hour and day, and its bytes per minute, hour and day."
SPEC.md: now "A machine that keeps up more than about 3.5 requests a second all day passes the day limit, as two busy Actions runners on one address do." Judgement call: this names the day limit's rate rather than "busier than that fleet". The fleet makes 172,800 a day, so a machine only a little busier stays under 300,000.
Model: opus-5-5
Rework of the four review findings:
1. `README.md`: now says counting cannot catch "a client slower than about 3.5 requests a second".
2. `docs/alerts.md`: the example ban is now at 12:50:00, for a crawler at 10 a second since 12:00. `time` and `ban_expires` moved to match.
3. `SPEC.md` "Purpose": now "It limits each client's requests per hour and day, and its bytes per minute, hour and day."
4. `SPEC.md`: now "A machine that keeps up more than about 3.5 requests a second all day passes the day limit, as two busy Actions runners on one address do." Judgement call: this names the day limit's rate rather than "busier than that fleet". The fleet makes 172,800 a day, so a machine only a little busier stays under 300,000.
Model: opus-5-5
Recorded (sneak, here): there must be a per-minute limit. On 200-300 a minute: yes, 300 rather than 200. An automation fleet of about 20 agents makes about 120 requests a minute at its busiest. A person browsing Gitea with a warm cache makes well under 100, but a page opened with an empty cache loads about 40 files (scripts, styles, avatars), so 200 leaves little room for a few quick first visits. This PR now builds 300 a minute as the default, beside 30,000 an hour and 300,000 a day; a different number is a one-line change.
Model: opus-5-5
Recorded (sneak, here): there must be a per-minute limit. On 200-300 a minute: yes, 300 rather than 200. An automation fleet of about 20 agents makes about 120 requests a minute at its busiest. A person browsing Gitea with a warm cache makes well under 100, but a page opened with an empty cache loads about 40 files (scripts, styles, avatars), so 200 leaves little room for a few quick first visits. This PR now builds 300 a minute as the default, beside 30,000 an hour and 300,000 a day; a different number is a one-line change.
Model: opus-5-5
clawbot
changed title from Default request limits per hour and per day, sized for API use (closes #176) to Default request limits per minute, hour and day, sized for API use (closes #176)2026-11-04 21:17:00 +01:00
Reworked for sneak's ruling: SWWAF_RATE_LIMIT_PER_MINUTE now defaults to 300. The tests and every document this PR touched follow it, and no sentence says requests go unlimited per minute any more. The docs/alerts.md example is now a minute-limit ban: a crawler at 10 a second, banned at its 301st request.
For sneak: at 300 a minute no client can reach the hour limit of 30,000, since 300 a minute is at most 18,000 an hour. The hour limit stays at 30,000 as ruled, and the documents say it only matters when the minute limit is raised or off.
Model: opus-5-5
Reworked for sneak's ruling: `SWWAF_RATE_LIMIT_PER_MINUTE` now defaults to `300`. The tests and every document this PR touched follow it, and no sentence says requests go unlimited per minute any more. The `docs/alerts.md` example is now a minute-limit ban: a crawler at 10 a second, banned at its 301st request.
For sneak: at 300 a minute no client can reach the hour limit of 30,000, since 300 a minute is at most 18,000 an hour. The hour limit stays at 30,000 as ruled, and the documents say it only matters when the minute limit is raised or off.
Model: opus-5-5
Reworked for sneak's ruling at #169 (comment): SWWAF_RATE_LIMIT_PER_MINUTE now defaults to 500, and the tests and every document this PR touches follow it.
The sentences saying the hour limit only matters with the minute limit raised or off are gone. The minute limit's reasons now follow the proposal. The docs/alerts.md ban now comes at the 501st request, 50 seconds in.
For sneak: 500 a minute is exactly 30,000 an hour, so at the defaults the minute limit bans a client before it can pass the hour limit.
Model: opus-5-5
Reworked for sneak's ruling at https://git.eeqj.de/sneak/smallwebwaf/issues/169#issuecomment-167897: `SWWAF_RATE_LIMIT_PER_MINUTE` now defaults to `500`, and the tests and every document this PR touches follow it.
The sentences saying the hour limit only matters with the minute limit raised or off are gone. The minute limit's reasons now follow the proposal. The `docs/alerts.md` ban now comes at the 501st request, 50 seconds in.
For sneak: 500 a minute is exactly 30,000 an hour, so at the defaults the minute limit bans a client before it can pass the hour limit.
Model: opus-5-5
README.md line 23, SPEC.md line 385 and docs/settings.md line 106 write "Gitea". Everywhere else, the repository writes it as gitea, in every document and in the name docs/gitea.md. Acceptable: "gitea" in all three sentences.
Model: opus-5-5
Review failed (`needs-rework`):
1. `README.md` line 23, `SPEC.md` line 385 and `docs/settings.md` line 106 write "Gitea". Everywhere else, the repository writes it as `gitea`, in every document and in the name `docs/gitea.md`. Acceptable: "gitea" in all three sentences.
Model: opus-5-5
SPEC.md line 391 ("A crawler that keeps up 4 a second makes 345,600 a day") and docs/settings.md line 114 ("The default catches a crawler that keeps up 4 a second around the clock") call this client a crawler. Everywhere else the repository calls it a scraper, including the README bullet this change edits ("Scrapers that fetch every page of a site, all day") and the README's "Rationale" ("scrapers that ignore robots.txt and crawl every commit"). A second name suggests a second kind of client. Acceptable: "scraper" in both sentences.
Model: opus-5-5
Review failed (`needs-rework`):
1. `SPEC.md` line 391 ("A crawler that keeps up 4 a second makes 345,600 a day") and `docs/settings.md` line 114 ("The default catches a crawler that keeps up 4 a second around the clock") call this client a crawler. Everywhere else the repository calls it a scraper, including the README bullet this change edits ("Scrapers that fetch every page of a site, all day") and the README's "Rationale" ("scrapers that ignore `robots.txt` and crawl every commit"). A second name suggests a second kind of client. Acceptable: "scraper" in both sentences.
Model: opus-5-5
Third failed review. Instructions that end the loop:
The rework changes exactly two words, nothing else:
SPEC.md line 391: "A crawler that keeps up 4 a second" becomes "A scraper that keeps up 4 a second".
docs/settings.md line 114: "crawler that keeps up 4 a second around the clock" becomes "scraper that keeps up 4 a second around the clock".
Then make fmt (the lines may rewrap), one commit amended with its message unchanged, rebased onto the current next.
It leaves alone every number, every other sentence, the code, the tests, the commit message and the PR body. All three reviews passed the arithmetic and the code; their findings were wording only, and the first two are fixed.
The next review checks only: that the change since d2c2408 is those two words and any rewrapping make fmt did; that the findings of the three reviews stay fixed; and make check on the tree rebased onto the current next. A new finding on a line those reviews already read stands only if the line says something false or contradicts the code. Choices of wording that the repository's documents do not settle are not findings.
Model: opus-5-5
Third failed review. Instructions that end the loop:
**The rework changes exactly two words**, nothing else:
- `SPEC.md` line 391: "A crawler that keeps up 4 a second" becomes "A scraper that keeps up 4 a second".
- `docs/settings.md` line 114: "crawler that keeps up 4 a second around the clock" becomes "scraper that keeps up 4 a second around the clock".
Then `make fmt` (the lines may rewrap), one commit amended with its message unchanged, rebased onto the current `next`.
**It leaves alone** every number, every other sentence, the code, the tests, the commit message and the PR body. All three reviews passed the arithmetic and the code; their findings were wording only, and the first two are fixed.
**The next review checks only**: that the change since `d2c2408` is those two words and any rewrapping `make fmt` did; that the findings of the three reviews stay fixed; and `make check` on the tree rebased onto the current `next`. A new finding on a line those reviews already read stands only if the line says something false or contradicts the code. Choices of wording that the repository's documents do not settle are not findings.
Model: opus-5-5
SPEC.md and docs/settings.md now say "scraper" instead of "crawler" for the client that keeps up 4 requests a second; make fmt rewrapped nothing.
Model: opus-5-5
`SPEC.md` and `docs/settings.md` now say "scraper" instead of "crawler" for the client that keeps up 4 requests a second; `make fmt` rewrapped nothing.
Model: opus-5-5
Yes, 20,000 an hour is enough. 30,000 was sized at about four times an automation fleet's busiest hour, which is more headroom than a well-behaved client needs. An automation fleet of about 20 agents makes about 7,200 an hour, so 20,000 still leaves it almost three times that. With 500 a minute, the minute limit catches bursts and the hour limit catches anything that keeps up more than about 5.5 a second. This PR now sets 20,000 an hour; the day limit stays at 300,000.
Model: opus-5-5
Yes, 20,000 an hour is enough. 30,000 was sized at about four times an automation fleet's busiest hour, which is more headroom than a well-behaved client needs. An automation fleet of about 20 agents makes about 7,200 an hour, so 20,000 still leaves it almost three times that. With 500 a minute, the minute limit catches bursts and the hour limit catches anything that keeps up more than about 5.5 a second. This PR now sets 20,000 an hour; the day limit stays at 300,000.
Model: opus-5-5
sneak's ruling above sets the hour default to 20,000; the minute limit stays at 500 and the day limit at 300,000. These instructions replace the scope of my last comment for one more rework.
The rework changes exactly this, nothing else:
internal/config/config.go line 510: the default "30000" becomes "20000". The tests follow: internal/config/config_test.go lines 266 and 2379, internal/smallwebwaf/smallwebwaf_test.go line 1248.
README.md line 20: "30,000 an hour" becomes "20,000 an hour". Lines 25-26: "A client at the minute limit for a whole hour reaches the hour limit exactly." becomes "The hour limit catches a client that keeps up more than about 5.5 a second for an hour."
SPEC.md lines 388-390, the "Per hour" bullet, becomes: "Per hour: 20,000, about 5.5 requests a second for a whole hour. A client that keeps up 6 a second makes 21,600 in an hour, and passes it, at 360 a minute, under the minute limit. The fleet's 7,200 an hour is about a third of it." In the paragraph after the bullets, after "passes the minute limit.", add: "One that keeps up more than about 5.5 a second for an hour passes the hour limit."
docs/settings.md lines 108-111: "(default 30000, about 8 a second)" becomes "(default 20000, about 5.5 a second)", and "A client at the minute limit for a whole hour reaches it exactly." becomes "It catches a client that keeps up more than about 5.5 a second for an hour."
docs/gitea.md line 39: before "Five busy at once", add "Three busy at once make 21,600 an hour and pass the hour limit of 20,000."
The commit message: "per hour: 30000, which a client at the minute limit for a whole hour reaches exactly;" becomes "per hour: 20000, passed by a client that keeps up 6 a second;", and "crawler" becomes "scraper". The PR body: 30000 becomes 20000, and the judgement call about 500 a minute being exactly 30,000 an hour goes.
Then make fmt, one commit amended, rebased onto the current next.
It leaves alone every other number, sentence, test and file.
The next review checks only: that the change since 776f9889 is the list above and any rewrapping make fmt did; the arithmetic of each changed sentence; that no sentence anywhere still gives the hour limit as 30,000 or says a client at the minute limit reaches it; the findings of the three reviews staying fixed; and make check on the tree rebased onto the current next. A finding on any other line stands only if the line says something false or contradicts the code.
Model: opus-5-5
sneak's ruling above sets the hour default to 20,000; the minute limit stays at 500 and the day limit at 300,000. These instructions replace the scope of my last comment for one more rework.
**The rework changes exactly this**, nothing else:
- `internal/config/config.go` line 510: the default `"30000"` becomes `"20000"`. The tests follow: `internal/config/config_test.go` lines 266 and 2379, `internal/smallwebwaf/smallwebwaf_test.go` line 1248.
- `README.md` line 20: "30,000 an hour" becomes "20,000 an hour". Lines 25-26: "A client at the minute limit for a whole hour reaches the hour limit exactly." becomes "The hour limit catches a client that keeps up more than about 5.5 a second for an hour."
- `SPEC.md` lines 388-390, the "Per hour" bullet, becomes: "Per hour: 20,000, about 5.5 requests a second for a whole hour. A client that keeps up 6 a second makes 21,600 in an hour, and passes it, at 360 a minute, under the minute limit. The fleet's 7,200 an hour is about a third of it." In the paragraph after the bullets, after "passes the minute limit.", add: "One that keeps up more than about 5.5 a second for an hour passes the hour limit."
- `docs/settings.md` lines 108-111: "(default `30000`, about 8 a second)" becomes "(default `20000`, about 5.5 a second)", and "A client at the minute limit for a whole hour reaches it exactly." becomes "It catches a client that keeps up more than about 5.5 a second for an hour."
- `docs/gitea.md` line 39: before "Five busy at once", add "Three busy at once make 21,600 an hour and pass the hour limit of 20,000."
- The commit message: "per hour: 30000, which a client at the minute limit for a whole hour reaches exactly;" becomes "per hour: 20000, passed by a client that keeps up 6 a second;", and "crawler" becomes "scraper". The PR body: `30000` becomes `20000`, and the judgement call about 500 a minute being exactly 30,000 an hour goes.
Then `make fmt`, one commit amended, rebased onto the current `next`.
**It leaves alone** every other number, sentence, test and file.
**The next review checks only**: that the change since `776f9889` is the list above and any rewrapping `make fmt` did; the arithmetic of each changed sentence; that no sentence anywhere still gives the hour limit as 30,000 or says a client at the minute limit reaches it; the findings of the three reviews staying fixed; and `make check` on the tree rebased onto the current `next`. A finding on any other line stands only if the line says something false or contradicts the code.
Model: opus-5-5
The default request limits now suit programmatic use of an API, such
as an automation fleet on another host, and still catch abusive bots:
- per minute: 500, room for a fleet's busiest minute and a few first
visits with an empty cache, and passed within seconds by a client
firing as fast as it can;
- per hour: 20000, passed by a client that keeps up 6 a second;
- per day: 300000, passed by a scraper that keeps up 4 a second.
The byte limits and ban lengths are unchanged. SPEC.md, README.md and
docs/settings.md give the reasons; docs/gitea.md redoes the Actions
runner sums.
Judgement call: the docs/alerts.md ban example shows the minute limit.
Model: opus-5-5
SWWAF_RATE_LIMIT_PER_HOUR now defaults to 20000, and the tests, the four documents, the commit message and the PR body follow the instructions at #179 (comment).
Model: opus-5-5
`SWWAF_RATE_LIMIT_PER_HOUR` now defaults to `20000`, and the tests, the four documents, the commit message and the PR body follow the instructions at https://git.eeqj.de/sneak/smallwebwaf/pulls/179#issuecomment-168093.
Model: opus-5-5
Judgement call: "keeps up more than about 5.5 a second for an hour" (README.md, SPEC.md, docs/settings.md) is read as a sustained rate, like the day limit's "all day"; with two buckets per window, a client that keeps it up for only one hour from mid-hour is not caught within that hour.
Model: opus-5-5
Review passed.
Judgement call: "keeps up more than about 5.5 a second for an hour" (`README.md`, `SPEC.md`, `docs/settings.md`) is read as a sustained rate, like the day limit's "all day"; with two buckets per window, a client that keeps it up for only one hour from mid-hour is not caught within that hour.
Model: opus-5-5
clawbot
merged commit 718a2c94b2 into next2026-11-04 23:02:40 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #176, with the numbers at #169 (comment).
SWWAF_RATE_LIMIT_PER_MINUTEnow defaults to500,SWWAF_RATE_LIMIT_PER_HOURto20000andSWWAF_RATE_LIMIT_PER_DAYto300000. Byte limits and ban lengths are unchanged. The tests of the defaults follow.SPEC.md, the README's "What it protects against" anddocs/settings.mdgive each default, its reason, and what counting cannot catch.docs/gitea.md: two Actions runners busy all day on one address pass the day limit; five busy at once pass the minute limit.docs/alerts.md: the ban example is a crawler at 10 a second, banned by the minute limit at its 501st request, 50 seconds in.What a reader might trip over:
SPEC.md's "Static lists" and "Byte limits, per client" changed too, since they relied on the old "40 busy people" sizing.SPEC.md), a client starting fresh can take longer than one window to pass a limit.Disclosures:
Model: opus-5-5
Review failed (
needs-rework):README.mdline 24: "Counting cannot catch a slower client" follows "4 a second", so it says a client below 4 a second gets through. The day limit also catches anything over about 3.5 a second: 3.6 a second makes 311,040 a day. Acceptable: "a client slower than about 3.5 requests a second", matchingSPEC.mdanddocs/settings.md("slower than these limits").docs/alerts.mdlines 38-65: the example ban comes 0.123 seconds into the hour, withcount30001 andrequests30001. The hour bucket starts on the hour, andrequestscounts every request since the client was first seen. So the earlier bucket was empty, and all 30,001 requests came in those 0.123 seconds. Acceptable: times that fit a crawler's pace, such as a request at 12:50:00 for 10 a second since 12:00, withtimeandban_expiresmoved to match.SPEC.mdline 18, in "Purpose": "It limits each client's requests and bytes per minute, hour and day." With no settings, requests are no longer limited per minute. Acceptable: "It limits each client's requests per hour and day, and its bytes per minute, hour and day."SPEC.mdline 395: "A busier machine that talks to the app all day can still pass the day limit." "Busier" has no stated comparison, and right after "an abuser that slow" it reads as busier than that abuser. Acceptable: name the comparison, such as "A machine busier than that fleet, such as two Actions runners busy all day, passes the day limit."Judgement call: the PR body, slightly over 250 words, is taken as within the limit.
Model: opus-5-5
1f71568d02to35054fe94bRework of the four review findings:
README.md: now says counting cannot catch "a client slower than about 3.5 requests a second".docs/alerts.md: the example ban is now at 12:50:00, for a crawler at 10 a second since 12:00.timeandban_expiresmoved to match.SPEC.md"Purpose": now "It limits each client's requests per hour and day, and its bytes per minute, hour and day."SPEC.md: now "A machine that keeps up more than about 3.5 requests a second all day passes the day limit, as two busy Actions runners on one address do." Judgement call: this names the day limit's rate rather than "busier than that fleet". The fleet makes 172,800 a day, so a machine only a little busier stays under 300,000.Model: opus-5-5
there absolutely needs to be a per-minute limit.
200-300 per minute max seems reasonable, no?
Recorded (sneak, here): there must be a per-minute limit. On 200-300 a minute: yes, 300 rather than 200. An automation fleet of about 20 agents makes about 120 requests a minute at its busiest. A person browsing Gitea with a warm cache makes well under 100, but a page opened with an empty cache loads about 40 files (scripts, styles, avatars), so 200 leaves little room for a few quick first visits. This PR now builds 300 a minute as the default, beside 30,000 an hour and 300,000 a day; a different number is a one-line change.
Model: opus-5-5
35054fe94bto3b9afd5ec4Default request limits per hour and per day, sized for API use (closes #176)to Default request limits per minute, hour and day, sized for API use (closes #176)Reworked for sneak's ruling:
SWWAF_RATE_LIMIT_PER_MINUTEnow defaults to300. The tests and every document this PR touched follow it, and no sentence says requests go unlimited per minute any more. Thedocs/alerts.mdexample is now a minute-limit ban: a crawler at 10 a second, banned at its 301st request.For sneak: at 300 a minute no client can reach the hour limit of 30,000, since 300 a minute is at most 18,000 an hour. The hour limit stays at 30,000 as ruled, and the documents say it only matters when the minute limit is raised or off.
Model: opus-5-5
3b9afd5ec4toa55b4c789cReworked for sneak's ruling at #169 (comment):
SWWAF_RATE_LIMIT_PER_MINUTEnow defaults to500, and the tests and every document this PR touches follow it.The sentences saying the hour limit only matters with the minute limit raised or off are gone. The minute limit's reasons now follow the proposal. The
docs/alerts.mdban now comes at the 501st request, 50 seconds in.For sneak: 500 a minute is exactly 30,000 an hour, so at the defaults the minute limit bans a client before it can pass the hour limit.
Model: opus-5-5
Review failed (
needs-rework):README.mdline 23,SPEC.mdline 385 anddocs/settings.mdline 106 write "Gitea". Everywhere else, the repository writes it asgitea, in every document and in the namedocs/gitea.md. Acceptable: "gitea" in all three sentences.Model: opus-5-5
a55b4c789ctod2c24085bbREADME.md,SPEC.mdanddocs/settings.mdnow write "gitea" in the three sentences that wrote "Gitea".Model: opus-5-5
Review failed (
needs-rework):SPEC.mdline 391 ("A crawler that keeps up 4 a second makes 345,600 a day") anddocs/settings.mdline 114 ("The default catches a crawler that keeps up 4 a second around the clock") call this client a crawler. Everywhere else the repository calls it a scraper, including the README bullet this change edits ("Scrapers that fetch every page of a site, all day") and the README's "Rationale" ("scrapers that ignorerobots.txtand crawl every commit"). A second name suggests a second kind of client. Acceptable: "scraper" in both sentences.Model: opus-5-5
Third failed review. Instructions that end the loop:
The rework changes exactly two words, nothing else:
SPEC.mdline 391: "A crawler that keeps up 4 a second" becomes "A scraper that keeps up 4 a second".docs/settings.mdline 114: "crawler that keeps up 4 a second around the clock" becomes "scraper that keeps up 4 a second around the clock".Then
make fmt(the lines may rewrap), one commit amended with its message unchanged, rebased onto the currentnext.It leaves alone every number, every other sentence, the code, the tests, the commit message and the PR body. All three reviews passed the arithmetic and the code; their findings were wording only, and the first two are fixed.
The next review checks only: that the change since
d2c2408is those two words and any rewrappingmake fmtdid; that the findings of the three reviews stay fixed; andmake checkon the tree rebased onto the currentnext. A new finding on a line those reviews already read stands only if the line says something false or contradicts the code. Choices of wording that the repository's documents do not settle are not findings.Model: opus-5-5
20k per hour is more than reasonable for well-behaved clients, no? why are we being so permissive?
d2c24085bbto776f988956SPEC.mdanddocs/settings.mdnow say "scraper" instead of "crawler" for the client that keeps up 4 requests a second;make fmtrewrapped nothing.Model: opus-5-5
Yes, 20,000 an hour is enough. 30,000 was sized at about four times an automation fleet's busiest hour, which is more headroom than a well-behaved client needs. An automation fleet of about 20 agents makes about 7,200 an hour, so 20,000 still leaves it almost three times that. With 500 a minute, the minute limit catches bursts and the hour limit catches anything that keeps up more than about 5.5 a second. This PR now sets 20,000 an hour; the day limit stays at 300,000.
Model: opus-5-5
sneak's ruling above sets the hour default to 20,000; the minute limit stays at 500 and the day limit at 300,000. These instructions replace the scope of my last comment for one more rework.
The rework changes exactly this, nothing else:
internal/config/config.goline 510: the default"30000"becomes"20000". The tests follow:internal/config/config_test.golines 266 and 2379,internal/smallwebwaf/smallwebwaf_test.goline 1248.README.mdline 20: "30,000 an hour" becomes "20,000 an hour". Lines 25-26: "A client at the minute limit for a whole hour reaches the hour limit exactly." becomes "The hour limit catches a client that keeps up more than about 5.5 a second for an hour."SPEC.mdlines 388-390, the "Per hour" bullet, becomes: "Per hour: 20,000, about 5.5 requests a second for a whole hour. A client that keeps up 6 a second makes 21,600 in an hour, and passes it, at 360 a minute, under the minute limit. The fleet's 7,200 an hour is about a third of it." In the paragraph after the bullets, after "passes the minute limit.", add: "One that keeps up more than about 5.5 a second for an hour passes the hour limit."docs/settings.mdlines 108-111: "(default30000, about 8 a second)" becomes "(default20000, about 5.5 a second)", and "A client at the minute limit for a whole hour reaches it exactly." becomes "It catches a client that keeps up more than about 5.5 a second for an hour."docs/gitea.mdline 39: before "Five busy at once", add "Three busy at once make 21,600 an hour and pass the hour limit of 20,000."30000becomes20000, and the judgement call about 500 a minute being exactly 30,000 an hour goes.Then
make fmt, one commit amended, rebased onto the currentnext.It leaves alone every other number, sentence, test and file.
The next review checks only: that the change since
776f9889is the list above and any rewrappingmake fmtdid; the arithmetic of each changed sentence; that no sentence anywhere still gives the hour limit as 30,000 or says a client at the minute limit reaches it; the findings of the three reviews staying fixed; andmake checkon the tree rebased onto the currentnext. A finding on any other line stands only if the line says something false or contradicts the code.Model: opus-5-5
776f988956to8bdffbd418SWWAF_RATE_LIMIT_PER_HOURnow defaults to20000, and the tests, the four documents, the commit message and the PR body follow the instructions at #179 (comment).Model: opus-5-5
Review passed.
Judgement call: "keeps up more than about 5.5 a second for an hour" (
README.md,SPEC.md,docs/settings.md) is read as a sustained rate, like the day limit's "all day"; with two buckets per window, a client that keeps it up for only one hour from mid-hour is not caught within that hour.Model: opus-5-5