fix(relay): require all three NIP-29 discovery kinds before treating a channel as reconciled (#3460) - #4039
fix(relay): require all three NIP-29 discovery kinds before treating a channel as reconciled (#3460)#4039iroiro147 wants to merge 1 commit into
Conversation
…a channel as reconciled (block#3460) When a channel was created via direct SQL (e.g. test seed), its kind:39000 metadata event was emitted at creation with the then-empty member list. If a later kind:39002 (members) emission failed after a join — and the join's emit_group_discovery_events tolerates failure — every subsequent reconcile pass only probed for 39000, classified the channel as reconciled, and left 39002 missing forever. The relay-side NIP-29 discovery stream then omitted pubkeys the channel_members table held, and the client's get_channels('#p': my key] query returned empty on cold boot, hiding the channel from the sidebar. Probe one addressable event per required kind (39000 metadata, 39001 admins, 39002 members) during reconcile_channel_events and re-emit all of them whenever any is missing. Fail closed on a query error (skip the channel) — never re-emit into a degrade this boot. reconcile_channel_events now requires all three discovery kinds before calling a channel reconciled. Unit test: pins [39000, 39001, 39002] as REQUIRED_DISCOVERY_KINDS and documents the probe order; buzz-relay lib suite 794 pass, 9 pre-existing api::media/admin environment failures (unchanged baseline). Signed-off-by: iroiro147 <sarthak.singh@juspay.in>
|
Independent reproduction confirming this root cause, and confirming the bug is still live on current Setup: self-hosted relay (Docker Compose, Relay-level membership is correct (rules out an identity/auth issue): $ buzz-admin list-members Join event is accepted by the relay: $ buzz channels join --channel Immediate re-query of the same channel omits the joining pubkey: $ buzz channels members --channel Tested on two builds several weeks apart — self-built from This lines up exactly with the root cause described here: the join's Happy to test this fix against my own setup once merged, or provide relay logs / DB |
|
@AIAlhello thank you — this is a particularly valuable repro because it rules out the two alternative explanations at the relay level: Your confirmation that it's still live on current |
Summary
Tightens the boot-time channel-discovery reconcile so a channel is only considered "reconciled" when all three NIP-29 discovery events (metadata 39000, admins 39001, members 39002) are present — not just 39000. Fixes the #3460 symptom where a channel a user had joined disappeared from their Desktop sidebar after a relaunch.
Root cause
Emission of the relay-signed discovery events is out-of-band and best-effort. Every site that changes membership or metadata tries to emit, but tolerates failure — e.g.
handle_join_requestwarn!-logs and proceeds whenemit_group_discovery_eventserrors (side_effects.rs). That means a channel can exist whose metadata event (kind:39000) was published at creation (when the member list was empty) while the members event (kind:39002) was rolled back or never written.The reconcile pass at boot previously probed only for kind:39000 to decide whether a channel needed (re-)emission. Once 39000 existed, every subsequent startup classified the channel as reconciled and skipped re-emission — so a channel whose 39002 never landed stayed broken forever. The relay's published member list then omitted pubkeys the
channel_memberstable held, the client'sget_channels({kinds:[39002],"#p":[my_key]})query returned empty on cold boot,isMemberderivedfalse, and the sidebar dropped the channel behind a "Join" prompt. This matches the reporter's own follow-up note that the relay's 39002 member-list event never included their pubkey even though the DB row was correct.Fix
reconcile_channel_eventsnow probes one addressable event per required kind (REQUIRED_DISCOVERY_KINDS = [39000, 39001, 39002]) for each channel, and re-emits the completeemit_group_discovery_eventssnapshot whenever any of the three is missing. Re-emission is idempotent (it replaces the current snapshot from the DB), so catching the "39002 was never written" shape also repairs the "39001 (admins) got lost" shape for free.Fail-closed on a probe query error: the channel is skipped rather than re-emitting into a shaky DB (matches the existing error-tolerance of this boot path). Probe cost is one query per kind per channel, once per boot.
Test
reconcile_probes_metadata_admins_and_members_kindspinsREQUIRED_DISCOVERY_KINDS == [39000, 39001, 39002](order + set + distinctness) so the revert can't regress silently.api::media/api::admin, unchanged baseline; 0 media/admin files touched in this diff).Environment note
End-to-end validation (a scratch Postgres with a channel whose 39002 is absent) requires the live relay Postgres fixture this worktree lacks. The change is behaviorally additive: channels that were already fully reconciled are untouched; only previously-stranded 39001/39002-missing channels get an initial emission at boot.