Skip to content

The report relay has brakes, and they count nobody's address - #47

Merged
JIRPOS merged 1 commit into
masterfrom
report-relay-rate-limit
Aug 14, 2026
Merged

The report relay has brakes, and they count nobody's address#47
JIRPOS merged 1 commit into
masterfrom
report-relay-rate-limit

Conversation

@JIRPOS

@JIRPOS JIRPOS commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Review Notes

ADDED: The KV binding the rate limiter reads its state from, which had been left commented out — the caps in [vars] were never reached without it, so the public endpoint had no brakes at all.
ADDED: rotate-rl-salt.sh, which generates the key the per-IP counters are hashed under and hands it to Cloudflare without printing it or writing it down.
ADDED: Tests for the whole-relay cap, the number of KV writes a report and a refusal each cost, the hashing, and the hourly pruning.
CHANGED: An address is stored as HMAC-SHA-256(RL_SALT, address) truncated to 12 bytes, never as itself.

  • HMAC and not a plain digest because an IPv4 address is 32 bits: a hash of one is a lookup table away from being the address again, which would have looked like protection without being any.

CHANGED: Both caps share one rl:<date> entry, so an accepted report costs one KV write and a refused one costs none. Entries older than an hour are dropped on the next write, which is also what bounds the value's size.
CHANGED: The whole-relay cap is 600 reports a day, up from 300.

  • One write per report makes the cap the day's write budget directly. The free tier allows 1000, and the 400 left over are margin rather than slack: a limiter that cannot write fails open, and a KV read can be a minute stale, so both caps are approximate in the direction of letting too much through.

CHANGED: PRIVACY.md describes the two counters and the keyed hash. It said the Worker stores nothing, which was true only while the limiter was switched off.
REMOVED: bump, the two-key counter helper.

ADDED: The KV binding the rate limiter reads its state from, which had been left commented out — the caps in `[vars]` were never reached without it, so the public endpoint had no brakes at all.
ADDED: `rotate-rl-salt.sh`, which generates the key the per-IP counters are hashed under and hands it to Cloudflare without printing it or writing it down.
ADDED: Tests for the whole-relay cap, the number of KV writes a report and a refusal each cost, the hashing, and the hourly pruning.
CHANGED: An address is stored as `HMAC-SHA-256(RL_SALT, address)` truncated to 12 bytes, never as itself.

- HMAC and not a plain digest because an IPv4 address is 32 bits: a hash of one is a lookup table away from being the address again, which would have looked like protection without being any.

CHANGED: Both caps share one `rl:<date>` entry, so an accepted report costs one KV write and a refused one costs none. Entries older than an hour are dropped on the next write, which is also what bounds the value's size.
CHANGED: The whole-relay cap is 600 reports a day, up from 300.

- One write per report makes the cap the day's write budget directly. The free tier allows 1000, and the 400 left over are margin rather than slack: a limiter that cannot write fails open, and a KV read can be a minute stale, so both caps are approximate in the direction of letting too much through.

CHANGED: `PRIVACY.md` describes the two counters and the keyed hash. It said the Worker stores nothing, which was true only while the limiter was switched off.
REMOVED: `bump`, the two-key counter helper.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JIRPOS
JIRPOS merged commit 5bdc3c8 into master Aug 14, 2026
9 checks passed
@JIRPOS
JIRPOS deleted the report-relay-rate-limit branch August 14, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant