Files
buzz/scripts
Logan Johnson a3909acc67 feat(relay): match label and creator filters before the page limit (#8176)
🤖
## Summary
After parts 1 and 2, a client can ask for "channels with label X" or
"channels created by key K". But the relay applied these tag filters
after it read one page of the newest channel events. So a request such
as `{kinds:[39000], #t:["workspace"], limit:10}` could return fewer than
10 channels, or none, while matching channels existed. This PR moves the
label and creator match into the database query, before the page limit.

Part 3 of 3, stacked on part 2.

### Related issue
None found.

### Details
- Applies only when every kind in the filter is a relay-signed
channel-state kind (39000–39003). Other kinds keep today's behavior.
- The database match uses JSONB containment on `tags`, which is
GIN-indexed. Containment can match a little too much (for example, a tag
that contains both strings in another position), never too little. So
the relay still checks each returned event with the normal Nostr filter
rules, and `COUNT` requests keep that check too.
- The same match is applied to the query path and the count path.
- **Spec.** NIP-CL now says that `#t` and `#P` match before `limit`.
- **Choosing one channel.** The relay does not make labels unique, so
two creates for one label can both succeed, for example when two
processes of one agent start at the same time. NIP-CL gets a "Choosing
one channel" section: a client that needs one channel per creator and
label looks it up with `#P` + `#t`, creates it only if none exists, and
otherwise uses the oldest one (the lowest `created_at` tag, then the
lowest channel ID). Every reader that follows this rule picks the same
channel, even after an edit.

### Testing
- Relay test: many channels, one match, small `limit`. The request
returns the match for both `#t` and `#P`. This test fails without the
change.
- Unit test: `#t` and `#P` reach the database query only for
channel-state kinds.
- Relay test: one creator sends two creates for the same label at once.
Both channels exist, a `#P` + `#t` lookup finds both, and the
oldest-channel rule picks the same one in either order, even after the
older channel's metadata is re-published by an edit.

### Stack
These are two stacks. Merge them in this order. Client support
(creating, labeling and finding these channels) will be in
block/buzz-app, not the buzz SDK and CLI.

**Stack 1: channel labels and creator lookup (#8174–#8176)**

1. #8174 channel type and creator tags, and the channel creation time
2. #8175 channel labels
3. #8176 database-side label and creator match, and the rule for
choosing one channel ← this PR

**Stack 2: system channels (#8180–#8182)**, stacked on stack 1 because
it uses labels and the creator tag

4. #8180 the system channel type
5. #8181 leave system channels out of reads that do not name them
6. #8182 channel-state filters opt in to system channels

---------

Signed-off-by: Logan Johnson <loganj@squareup.com>
Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
2026-10-14 19:03:34 -04:00
..