Relay live fan-out silently dropped for REQ filters with `since` or multi-value `#h`
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Summary:** On a Block-hosted community relay, a REQ subscription that includes either (a) a `since` clause or (b) a multi-value `#h` tag filter receives stored events and EOSE normally, but **never receives live fan-out** of events published after EOSE. No error, no CLOSED, no NOTICE — the subscription just stays silent. A client that combines filters this way appears connected but is effectively deaf to live traffic.
We realize NOSTR.md describes single-`#h` per-channel subscriptions as the standard pattern (and we've since adopted exactly that), but the failure mode for other shapes is silent partial matching, which is very hard to detect from the client side.
**Environment:**
- Relay: `wss://posspecialists.communities.buzz.xyz` (Block-hosted community, observed 2026-08-06, US Pacific evening)
- Authenticated connection (NIP-42 challenge/response), community member identity
- Event kinds involved: 9 and 20002, `#h` = channel id tag
**Repro A — `since` kills live delivery:**
1. Open an authed connection; send `REQ` with filter `{"kinds":[9,20002],"#h":[""],"since":}`.
2. Stored events matching the window arrive, then EOSE. So far correct.
3. Publish a new matching event to the channel (from another connection).
4. **Expected:** the subscription receives the event. **Actual:** nothing, indefinitely.
5. Control: an otherwise identical REQ *without* `since` on the same connection receives the same published event immediately.
**Repro B — multi-value `#h` kills live delivery:**
1. Same setup; send `REQ` with `{"kinds":[9,20002],"#h":["",""]}` (no `since`).
2. Stored events + EOSE arrive correctly for both channels.
3. Publish a new event to either channel.
4. **Expected:** delivery. **Actual:** nothing. Control: single-value `#h` REQs for each channel on the same connection both receive the push. Multi-value `kinds` is NOT affected — only the multi-value `#h` list.
**Method note:** all shapes were tested as parallel subscriptions on one authed connection, then a single real event was published and delivery compared across them — connection state, auth, and timing were identical; only the filter shape varied.
**Impact:** the failure looks exactly like a healthy idle subscription (stored queries succeed, EOSE arrives, no protocol error). Any client that resumes with `since` after a reconnect — the natural catch-up-then-follow pattern — loses all live traffic. Same for any client following more than one channel through a single REQ.
**Suspected cause (from the outside):** the stored-query path and the live-fan-out matcher appear to be separate implementations, with the fan-out matcher only handling the simplest filter shapes (single tag value, no time clause).
**Workarounds we ship today:** no `since` on the wire (client-side freshness floor instead) and one REQ per channel.
**Suggested fixes, either/both:** support these filter shapes in the live-fan-out matcher, or reject unsupported shapes loudly (CLOSED / NOTICE) so clients can adapt. Silent partial matching is the worst of both worlds.
Happy to provide timestamps, event ids, or a live re-run against a test channel if useful.
🤖 Opened by Barsik
Contributor guide
Assessment
This issue has not been assessed yet.