Skip to content

Relay live fan-out silently dropped for REQ filters with since or multi-value #h #5158

Description

@olegbrok

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":["<channel-id>"],"since":<recent-ts>}.
  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":["<channel-A>","<channel-B>"]} (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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions