diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/buzz/README.md b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/buzz/README.md index 1712dd3c2e..cd8bc2c7ac 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/buzz/README.md +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/buzz/README.md @@ -25,6 +25,37 @@ The prose specs under Buzz's `docs/nips/` are **drafts and lag the code**. Every here is confirmed against the authoritative Rust — `crates/buzz-core` (the per-kind modules), `crates/buzz-sdk` (event builders, `nip_oa.rs`) — not the markdown. +### Reading Buzz's source without a checkout + +KDoc across this package cites Rust files as `buzz-relay/src/handlers/side_effects.rs`. +Those are **crate-relative**: on disk everything lives under `crates/`, so the real path is +`crates/buzz-relay/src/handlers/side_effects.rs`. Prefix accordingly or the fetch 404s. + +`gh` is the way in (the repo is public, but `raw.githubusercontent.com` 404s on these paths +and plain `curl` is usually sandboxed): + +```bash +gh api -X GET search/code -f q='emit_system_message repo:block/buzz' --jq '.items[].path' +gh api repos/block/buzz/contents/crates/buzz-relay/src/handlers/side_effects.rs --jq '.content' | base64 -d +``` + +Worth knowing where the answers tend to live: + +- `crates/buzz-relay/src/handlers/` — `side_effects.rs` (what the relay emits and when: + system messages, discovery, thread summaries), `event.rs` (ingest + acceptance), + `command_executor.rs` (the 90xx command verbs). +- `crates/buzz-db/src/channel.rs` — the channel row: `channel_type` (`stream` / `forum` / + `dm` / `workflow`) and `visibility` (exactly two values, `open` = searchable + anyone can + join, `private` = hidden + invite-only). +- `desktop/src/features/**` — what Buzz's own client actually *renders*, which is what + decides whether an event we publish is visible to anyone else. Worth checking before + adding a write path: e.g. forum posts only ever surface in `channelType === "forum"`. + +The relay's **tests are the best spec** — several encode invariants as named assertions. +`channel_scoped_content_kinds_require_h_tags` in `handlers/event.rs` is the one to know: +canvas (40100) and the forum kinds (45001/45002/45003) are per-**channel**, never +per-workspace, because an `h` tag is mandatory on all of them. + Compliance is verified against **vectors generated by Buzz's own code**, not hand-transcribed schemas: @@ -64,17 +95,33 @@ conflicts below). | `presence` / `huddles` / `pairing` / `audit` / `media` | presence + misc | 20001, 20002 / 24810, 48100-48106 / 24134 / 48001 / 49001 | | `rsReadState` | NIP-RS | (helpers on `AppSpecificDataEvent`, kind 30078) | -### Kind conflicts — implemented but NOT registered in EventFactory +### Kind conflicts — where a Buzz kind number is already owned -These Buzz kind numbers are already owned by an existing Amethyst/Nostr class, so the -Buzz model exists (build/parse it explicitly) but the incumbent keeps the `EventFactory` -dispatch slot: +Several Buzz kind numbers collide with an existing Amethyst/Nostr class. `EventFactory` +resolves what it can by **tag shape inside the kind's block** — the two meanings never +coexist on one relay, and each carries a tag the other never emits, so one `if` inside the +branch routes both. The rest keep the incumbent and are built/parsed explicitly. + +**Disambiguated — both classes reachable:** + +| Kind | Discriminator | Present → | Absent → | +|---|---|---|---| +| 20001 | `g` (geohash) tag | `bitchat.geohash.GeohashPresenceEvent` | `presence.PresenceUpdateEvent` | +| 39005 | `h` (channel) tag | `cwChannelWindow.ThreadSummaryEvent` | `nip29RelayGroups.metadata.GroupPinnedEvent` | + +Both discriminators are load-bearing in *both* directions, since outbound signing goes +through the same factory — check any new one against the two builders as well as the wire. +For 39005 the signal is that the whole NIP-29 relay-generated 39xxx family (metadata, +admins, members, participants, supported-roles, pinned) is addressed by `d` alone and never +emits `h`, while a Buzz thread summary always carries one. Nothing throws on a mismatch, so +getting this wrong is silent: a summary parsed as a pin list would have reported the thread +root as a pinned message. Covered by `nip29RelayGroups/PinEventsTest`. + +**Incumbent keeps the slot — build/parse the Buzz model explicitly:** | Kind | Buzz class | Incumbent (registered) | |---|---|---| | 9041 | `moderation.ModerationUnbanEvent` | `nip75ZapGoals.GoalEvent` | -| 20001 | `presence.PresenceUpdateEvent` | `experimental.bitchat.geohash.GeohashPresenceEvent` | -| 39005 | `cwChannelWindow.ThreadSummaryEvent` | `nip29RelayGroups.metadata.GroupPinnedEvent` | | 49001 | `media.MediaUploadEvent` | — (Buzz's own `kind.rs` marks 49001 "Not a relay event kind") | | 30078 | `rsReadState` (helpers) | `nip78AppData.AppSpecificDataEvent` (NIP-RS reuses 30078) |