Subscriptions

Every filter in a REQ must say what it is looking for. Filters that would stream the whole lane are rejected.

Accepted and rejected filters

FilterResult
{"kinds":[1078], "#t":["lobby"]}Accepted — any tag except #p
{"kinds":[31081], "#d":["deck"]}Accepted
{"authors":["<pubkey>"]}Accepted (up to 1024 authors)
{"#p":["<your pubkey>"]}Accepted after AUTH
{"#p":["<someone else>"]}Rejected, with or without AUTH
{"kinds":[20000]}, {}Rejected — no scope
{"#t":[]}Rejected — an empty tag list matches nothing

A rejected subscription is answered with ["CLOSED", <id>, "restricted: …"]. If one filter in a REQ is rejected, the whole REQ is.

Why the rules exist

A filter without scope turns the relay into a firehose: it costs the most and tells a watcher everything happening in the lane. #p is limited to yourself because it reveals who is being contacted, when, and by whom.

Limits

  • At most 16 open subscriptions per connection; the 17th is closed with rate-limited:.
  • At most 1024 stored events are returned per filter; a larger limit is reduced.
  • Stored events are returned newest first, followed by EOSE; new matching events then arrive live.