fix(relay): route live REQ events by each filter's own channel scope

A live REQ took one request-wide routing scope from its filters' aggregate
`#h`. If any filter lacked `#h`, the whole subscription became global, so:

- `{kinds:[39000], "#d":[ch]}` got history and EOSE but never a later
  replacement, because relay-signed group-state events are stored under
  their channel and global subscriptions never see channel events;
- in a mix of named and unnamed filters, the named filter lost live
  delivery too.

Each filter now carries its own server-resolved live scope. A filter
with `#h` routes under the channels it names and may read. A filter
without `#h` keeps the global topic, and a group-state filter also routes
under the readable channels its `#d` names. Fan-out delivers only through
a filter that both matches the event and holds its channel, so a channel
one filter names never reaches a `#P` or `#t` sibling (the confinement
from #8469). The subscription retains each topic in its filters' union
once; CLOSE, same-ID replacement and revocation release it once.

Also runs the subscription registry unit tests in the unit lanes; no
lane selected them before.

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
This commit is contained in:
Logan Johnson
2026-10-21 09:47:59 -04:00
parent cd8a968179
commit 3255e910ef
7 changed files with 1026 additions and 342 deletions

View File

@@ -251,6 +251,9 @@ run_unit_tests() {
run_test_step "buzz-relay NIP-FI resolver tests" \
cargo test -p buzz-relay --lib nip_fi_resolver:: -- --nocapture
run_test_step "buzz-relay subscription registry tests" \
cargo test -p buzz-relay --lib subscription::tests:: -- --nocapture
# Infra-free REQ subscription-lifecycle tests, by exact name: the rest of
# handlers::req needs a database.
run_test_step "buzz-relay REQ subscription lifecycle tests" \