Last week I banned my own phone from my own relay (nos.lol).
On the relay there is a per-IP cap, which allows 200 REQ messages every 10 seconds. Then an automated script bans addresses that keep blowing past it.
A flagged address had sent 283 over-cap REQs from a single websocket and reconnected 75 times in an hour. Then I realized it was actually me, checking my relays' global feeds in YakiHonne web and Coracle! Normal reading, two heavy clients, one CGNAT (mobile) address.
Then told my clanker to read the REQ layer of the most popular clients. The cap counts REQ messages, so a client's economy is mostly about how it batches, caches, and re-issues subscriptions.
FINDINGS
There is a 100x difference among client's handlings of REQs.
DB-first (scroll ≈ 0 REQs):
Damus renders from nostrdb; one multi-filter REQ per relay per view, hard 14-sub cap (new subs pause, not drop). Amethyst batches 3 filters per REQ, re-sends under the relay's max-filters notice, keeps refusal memory after repeated CLOSED. Gossip: CLOSE on EOSE, disconnects idle relays after 10s, retried rate-limited subs, randomized reconnect backoff. Nostur: client-side 6 REQ/s limiter with CLOSE jumping the queue — unique and underrated.
Mixed:
Iris unions filters across components in 100ms windows, but leaked pagination subs replay on every reconnect. Nostrudel: clean EOSE→CLOSE lifecycle, but an auto-pager pulls history at ~1 REQ/s even while idle, and NIP-11 max_reqs is displayed, not enforced.
Heavy:
Snort's query engine is excellent, but every mounted note fires a reactions REQ (kinds 7/6/9735, #e-tagged, no limit) to every read relay, unsatisfiable from cache. YakiHonne web passes groupable:false to NDK — explicitly disabling its subscription merging — then fires 1 REQ per card-mount per relay, no dedup, re-fired on remount. Coracle (welshman) sends one filter per REQ, and on socket flap replays the full pending REQ set with backoff resetting to zero whenever the relay emits traffic — a rate-limiting, event-streaming relay is the worst case: instant reconnect, full replay, loop.
Common failure across heavy clients: CLOSED is unhandled — dropped (Damus), ignored (YakiHonne, Iris), or terminal with no backoff (Snort, Coracle). A relay saying "rate-limited" changes nothing about their sending pattern.
Excluded: Primal web — reads from Primal's caching service, not relays (0 direct REQs while reading). Different architecture, not comparable.
FOR USERS of MY RELAYS
Choose a well engineered client otherwise you may be banned temporariliy because the usage patterns may look like a flood.
