A firehose consumer whose TCP peer stalls keeps its slot and pins the replay ring until the ring evicts past it. Nothing times out a send, and nothing pings.
Measured on loopback at 73ddde8: two consumers that stopped reading were never closed during a 75 s run at 150 writes/s. The shared ring keeps up to twice its 4,096 frames (64 MiB byte cap) for unsent data, so a stalled consumer holds up to about 9–18 MB at 2.2 KB per frame. It also holds 1 of the 128 total slots (8 per address), which are already the binding limit for 230 consumers at 10x.
Fix: a send timeout per frame (or per batch), plus WebSocket pings with a deadline, closing the consumer with ConsumerTooSlow or a plain close when either expires. State both as config keys with ranges.
Fixed in #1239. Each frame must reach the socket within
[capacity] firehose_send_timeout_secs, and each stream pings everyfirehose_ping_secs(both default 30, range 5 to 300). A consumer that misses either is closed and its slot given back.