mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
docs(nostr): document #h requirement for live reaction subscriptions (#3487)
## What this fixes
`fan_out_scoped` (`crates/buzz-relay/src/subscription.rs:278-394`)
enforces a deliberate, symmetric scoping invariant — documented in the
code itself:
> Global subscriptions (channel_id = None) do NOT receive channel-scoped
events. Channel-scoped subscriptions do NOT receive global events.
The relay derives a reaction's stored channel from its `#e` target at
ingest — client-supplied `#h` is ignored for channel determination
(`NOSTR.md:50` documents this for *writing*). The consequence for
*reading* is that every reaction is a channel-scoped event, so a live
subscription `{"kinds":[7]}` without `#h` is a global subscription and
**silently receives no reactions at all** — no error, no CLOSED, just
nothing. The working form is `{"kinds":[7],"#h":["<channel-uuid>"]}`,
and it works regardless of how the reaction was signed: explicit `h`
tags on the event are matched directly, and tagless reactions match via
the stored channel fallback (`crates/buzz-core/src/filter.rs:78-91` —
fallback applies only when the event has no `h` tags; explicit tags are
authoritative).
`NOSTR.md` already documents this exact pitfall for group-metadata
events:
> **Note:** Channel-scoped storage means live global subscriptions
(`{kinds:[39000]}`) won't receive these via fan-out.
(`NOSTR.md:124-126`)
…but has no equivalent note for reactions, which is the case a
bot/integration author is far more likely to hit: any client that wants
to observe approvals/reactions live (workflow reaction-triggers make
this a first-class pattern in Buzz) will naturally try a kinds-only REQ
first and conclude reactions are broken. We lost real debugging time to
exactly this while building a headless integration
(https://github.com/OriginTrail/buzz-dkg-integration); the behavior is
by design, only the docs are missing.
## What this PR changes
Docs only (`NOSTR.md`): a subscribe-to-reactions example in "Sending
Messages", plus one note mirroring the existing 39000 note. No code
changes.
## How to verify
- Behavior: with the relay running, open a live REQ `{"kinds":[7]}` (no
`#h`) and react to a channel message from another client → nothing is
delivered; re-subscribe with `{"kinds":[7],"#h":["<channel-uuid>"]}` →
the reaction arrives.
- Claims against code (verified at `485d03a`): scoping invariant
`crates/buzz-relay/src/subscription.rs:386-393`; channel derivation
`derive_reaction_channel()` in
`crates/buzz-relay/src/handlers/ingest.rs`; `#h` fallback
`crates/buzz-core/src/filter.rs:78-91` and its test
`h_tag_fallback_uses_stored_channel_id`.
Duplicate search: no existing issue/PR found for `reactions
subscription`, `fan-out kinds` (searched 2026-07-29). DCO signed-off.
---------
Signed-off-by: Žiga Drev <ziga.drev@gmail.com>
Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Co-authored-by: Žiga Drev <ziga.drev@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
This commit is contained in:
co-authored by
Žiga Drev
Claude Opus 4.8
npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf
parent
36cf932ff0
commit
756dd7f65d
@@ -163,6 +163,10 @@ nak req -k 9 --tag "h=<channel-uuid>" --stream \
|
|||||||
nak event -k 7 -c "+" --tag "h=<channel-uuid>" --tag "e=<message-event-id>" \
|
nak event -k 7 -c "+" --tag "h=<channel-uuid>" --tag "e=<message-event-id>" \
|
||||||
--auth --sec <privkey> ws://localhost:3000
|
--auth --sec <privkey> ws://localhost:3000
|
||||||
|
|
||||||
|
# Subscribe to reactions to channel messages — include #h for live delivery (see note below)
|
||||||
|
nak req -k 7 --tag "h=<channel-uuid>" --stream \
|
||||||
|
--auth --sec <privkey> ws://localhost:3000
|
||||||
|
|
||||||
# Delete a message (#h optional; #e required; must be self-authored)
|
# Delete a message (#h optional; #e required; must be self-authored)
|
||||||
nak event -k 5 -c "reason" --tag "h=<channel-uuid>" --tag "e=<message-event-id>" \
|
nak event -k 5 -c "reason" --tag "h=<channel-uuid>" --tag "e=<message-event-id>" \
|
||||||
--auth --sec <privkey> ws://localhost:3000
|
--auth --sec <privkey> ws://localhost:3000
|
||||||
@@ -185,6 +189,14 @@ nak req -k 1059 --tag "p=<your-hex-pubkey>" \
|
|||||||
--auth --sec <privkey> ws://localhost:3000
|
--auth --sec <privkey> ws://localhost:3000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note:** The relay derives a reaction's channel from its `#e` target (client `#h` is
|
||||||
|
> ignored for channel determination). Reactions to channel-scoped events are therefore
|
||||||
|
> channel-scoped. Live fan-out keeps channel-scoped and global subscriptions strictly
|
||||||
|
> separate, which means a kinds-only subscription (`{"kinds":[7]}`) receives none of
|
||||||
|
> those reactions — subscribe with `{"kinds":[7],"#h":["<channel-uuid>"]}` instead.
|
||||||
|
> `#h` matching works whether or not the signed reaction carries an `h` tag: explicit
|
||||||
|
> `h` tags are matched directly, and tagless reactions match via their stored channel.
|
||||||
|
|
||||||
### Tested Clients (Direct)
|
### Tested Clients (Direct)
|
||||||
|
|
||||||
| Client | Platform | Evidence | Notes |
|
| Client | Platform | Evidence | Notes |
|
||||||
|
|||||||
Reference in New Issue
Block a user