Say which bytes the byte limits count, and what passing one does (closes #203)
check / check (push) Successful in 4m13s

Documents only. The byte limits and byte anomaly thresholds count body
bytes, as SWW_BYTES_COUNT chooses them, with WebSocket traffic each way,
once a response has ended; the request that passes a limit is not cut
off and the client is banned. README.md, SPEC.md and docs/ now say so,
and no longer call a response's bytes the request's. Also made plain:
rate limits, the refusal threshold, SWRP_MAX_REQUEST_TIME_MS,
SWW_LOOKUP_SOURCE, the metrics' byte series, and the SPEC.md claim about
answering within SWRP_TIMEOUT_MS. EVALUATION.md is audited too; its
survey facts and date are unchanged.

Model: fable-5-1 (first audit); opus-5-5 (rework)
This commit is contained in:
sneak committed 2026-11-09 01:33:48 +00:00
1 parent f6435b1a73
commit 3911b1611c
7 files changed
+228 -195

No files matched your search

+27 -19
View File
@@ -22,8 +22,8 @@ to them.
key or a token stays off until the operator supplies it.
- It keeps a site answering while the part that decides is down, slow or
restarting (see "Without an answer from `discriminator`").
- It limits each client's requests and bytes per minute, hour and day. It bounds
the size and duration of every request and answer. It detects attacks.
- It limits each client's requests and body bytes per minute, hour and day. It
bounds the size and duration of every request and answer. It detects attacks.
- It bans abusers: briefly at first, for seven days on a clear sign of attack,
and permanently when they keep at it.
- It looks up each client's AS number and country. It can refuse whole countries
@@ -184,11 +184,12 @@ request log's `heuristic` field gives that name. `refusal_limit` and
sent is removed.
15. After the visitor's answer has ended, `rproxy` sends the report (see "The
report"). `discriminator` then counts:
- For a request it allowed, the response bytes, the request bytes or both
are added to the byte counters, as `SWW_BYTES_COUNT` says. For a
WebSocket, what it carried each way counts once it closes. A client whose
total passes a byte limit times the percentage has broken that limit and
is banned (`byte_limits`). The response in progress is not cut off.
- For a request it allowed, the body bytes of the response, of the request
or of both are added to the byte counters, as `SWW_BYTES_COUNT` says. For
a WebSocket, what it carried each way counts once it closes. A client
whose total passes a byte limit times the percentage has broken that limit
and is banned (`byte_limits`). The request that broke it has already been
passed on whole; nothing is cut off.
- For a request it allowed, the client's history and the anomaly counters
are updated, and the alert thresholds are evaluated.
@@ -251,7 +252,8 @@ to a ban, each by its own rule:
clear sign of attack bans it permanently at once.
- A broken limit, one of:
- a request over a rate limit;
- a response that takes the client's byte total past a byte limit;
- the bytes counted for a request and its response taking the client's total
past a byte limit;
- more than `SWW_ERROR_BURST_THRESHOLD` refusals within a minute.
The bans for a broken limit:
@@ -439,10 +441,15 @@ the same way.
### Byte limits, per client
By default there is no minute limit, so a client can download in a burst at full
speed. The hour limit defaults to 50 GiB and the day limit to 100 GiB. They
allow several downloads of a few GiB each. A response's bytes are counted when
it ends (see step 15).
The byte limits count body bytes: the app's response bodies, the visitors'
request bodies, or both, as `SWW_BYTES_COUNT` says. Headers are never counted.
What the visitor sent over a WebSocket counts as request bytes, and what the app
sent as response bytes. The bytes counted for a request and its response are
added to the client's total once the response has ended (see step 15). So the
request that takes a client past a limit is passed on whole, and the ban refuses
the client's requests from then on. By default there is no minute limit, so a
client can download in a burst at full speed. The hour limit defaults to 50 GiB
and the day limit to 100 GiB. They allow several downloads of a few GiB each.
### Lookup of AS number and country
@@ -905,7 +912,8 @@ which the metrics show. They reveal a widely distributed scraper, thousands of
addresses at low rates each, which the per-client limits do not see. A low
`SWW_ASN_LIMIT_PERCENT` for its AS number is the response.
- Requests and bytes are counted per minute and per hour, for each of:
- Requests and body bytes, as `SWW_BYTES_COUNT` counts them, are counted per
minute and per hour, for each of:
- a client;
- the surrounding netblock;
- an AS number;
@@ -1215,9 +1223,9 @@ short.
### Answering in time
`rproxy` stops waiting for `discriminator` after `SWRP_TIMEOUT_MS`, by default 2
seconds, and `SWRP_DEFAULT_DISPOSITION` then decides. So at the defaults,
`discriminator` answers a request well within that time, whatever the request
and whatever its outside sources do.
seconds, and `SWRP_DEFAULT_DISPOSITION` then decides. So the longest a question
waits on an outside source, `SWW_LOOKUP_TIMEOUT`, is by default well within that
time, whatever the source does.
GeoJS is the only outside source a question can wait on. A question waits only
when step 5 says so, and for at most `SWW_LOOKUP_TIMEOUT`, by default 800
@@ -1313,9 +1321,9 @@ answer.
For a request it holds, `discriminator` writes the request's last log line (see
"The log"). For a request it allowed, it first:
- counts the bytes as `SWW_BYTES_COUNT` says, toward the client's byte limits at
the percentages it gave that request;
- bans a client that passes a byte limit;
- counts the body bytes `SWW_BYTES_COUNT` names, toward the client's byte limits
at the percentages it gave that request;
- bans a client whose total passes a byte limit;
- adds the request with its status to the client's history, the anomaly counters
and the metrics.