Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/block/buzz/llms.txt

Use this file to discover all available pages before exploring further.

Buzz runs a NIP-29 relay natively, which means any Nostr client that speaks NIP-29 (relay-based groups) and NIP-42 (authentication) can connect to it directly. You do not need the Buzz desktop or mobile app — nak, Chachi, 0xchat, and any other NIP-29 client all work over the standard WebSocket wire protocol.

Connecting to your relay

Point your Nostr client at the relay WebSocket URL:
ws://localhost:3000       # local development
wss://your-relay-domain   # self-hosted or Railway deploy
The relay will send a NIP-42 AUTH challenge immediately on connection. Your client must respond with a signed AUTH event before it can send or receive events.
Community domain routing. In a single-relay deployment, the relay URL selects exactly one community — all events are scoped to that community. In a multi-community deployment, each community is served by its own domain or subdomain. The backend derives the community from the HTTP Host header before handling AUTH, EVENT, REQ, REST, media, git, and search traffic. The Nostr wire format does not grow a tenant tag — your client connects to the right community by connecting to the right URL.

NIP-11 relay information

Query the relay’s metadata without a WebSocket connection:
curl -H "Accept: application/nostr+json" http://localhost:3000
The relay responds with a NIP-11 document describing its supported NIPs, software version, and (if set) workspace icon. The icon can be set by relay owners via a kind:9033 admin event.

Feature compatibility

These features work with any NIP-29 + NIP-42 compatible client:
FeatureKindNotes
Group chat9Requires #h <channel-uuid> tag
Reactions7NIP-25; channel derived from target’s #e — client #h ignored
Deletions5NIP-09; self-authored only; #e required
User profiles0NIP-01 metadata synced to display_name, avatar, about, NIP-05
Group creation9007Include name tag; optional visibility and channel_type
Add user9000Open: any user (subject to channel_add_policy). Private: owner/admin only
Remove user9001Self-remove allowed (last-owner guard). Others: owner/admin only
Edit group metadata9002name/about: owner/admin. topic/purpose: any member
Admin delete event9005Event author can always delete own; otherwise owner/admin required
Group deletion9008Owner only
Leave group9022Any member; last-owner guard prevents orphaned groups
Group metadata39000Relay-signed; d, name, closed always present
Group admins39001Relay-signed; p tags with owner/admin roles
Group members39002Relay-signed; p tags for all members
Membership notifications44100/44101Added/removed; relay-signed; community-global scope
Presence20001Ephemeral; status string truncated to 128 chars
Typing indicators20002Ephemeral, not stored; via Redis pub/sub
NIP-42 authenticationProactive challenge; optional pubkey allowlist
NIP-11 relay infoGET / with Accept: application/nostr+json
NIP-50 searchOne-shot REQs with "search":"query" → relevance-sorted results → EOSE
NIP-10 threadsReplies with ["e","<root>","","reply"] create thread_metadata atomically
NIP-17 DMs (gift wrap)1059Ephemeral signing keys accepted; community-globally scoped; delivered via #p filter
Blossom mediaPUT /media/upload (BUD-02), GET /media/{sha256}.{ext} (BUD-01)
Join request9021Open channels only; adds member, emits system message + discovery events

Sending messages with nak

nak is a CLI tool for sending and subscribing to Nostr events. It’s the fastest way to verify that your relay connection works without a full client.
# Send a group message
nak event -k 9 -c "Hello from NIP-29!" \
  --tag "h=<channel-uuid>" \
  --auth --sec <privkey> ws://localhost:3000

# Subscribe to channel messages (streaming)
nak req -k 9 --tag "h=<channel-uuid>" --stream \
  --auth --sec <privkey> ws://localhost:3000

# React to a message
nak event -k 7 -c "+" \
  --tag "h=<channel-uuid>" \
  --tag "e=<message-event-id>" \
  --auth --sec <privkey> ws://localhost:3000

# Delete a message (self-authored only)
nak event -k 5 -c "reason" \
  --tag "h=<channel-uuid>" \
  --tag "e=<message-event-id>" \
  --auth --sec <privkey> ws://localhost:3000

# Create a group
nak event -k 9007 \
  --tag "name=my-channel" \
  --tag "visibility=open" \
  --auth --sec <privkey> ws://localhost:3000

# Search messages (NIP-50)
nak req -k 9 --tag "h=<channel-uuid>" --search "search query" -l 20 \
  --auth --sec <privkey> ws://localhost:3000

# Reply to a message (NIP-10 threading)
nak event -k 9 -c "Reply text" \
  --tag "h=<channel-uuid>" \
  --tag "e=<parent-event-id>;;reply" \
  --auth --sec <privkey> ws://localhost:3000

# Discover all groups you can see
nak req -k 39000 --auth --sec <privkey> ws://localhost:3000

# Receive gift-wrapped DMs (NIP-17)
nak req -k 1059 --tag "p=<your-hex-pubkey>" \
  --auth --sec <privkey> ws://localhost:3000

Pubkey allowlist

When BUZZ_PUBKEY_ALLOWLIST=true, NIP-42 connections that authenticate with only a pubkey (no API token) are checked against the pubkey_allowlist table. This lets you open the relay to specific external Nostr identities without granting full access.
Fail-closed. If the database lookup fails, the connection is denied. Users with valid API tokens bypass the allowlist entirely. The auth failure message is a generic auth-required: verification failed — no allowlist-specific detail is leaked.
Manage the allowlist via direct SQL (no CLI command yet):
-- Add a pubkey
INSERT INTO pubkey_allowlist (pubkey)
VALUES (decode('<64-char-hex-pubkey>', 'hex'));

-- Remove a pubkey
DELETE FROM pubkey_allowlist
WHERE pubkey = decode('<64-char-hex-pubkey>', 'hex');

-- List all allowed pubkeys
SELECT encode(pubkey, 'hex'), added_at, note
FROM pubkey_allowlist;
Or using the quick-start pattern from NOSTR.md:
# Enable allowlist (must be set BEFORE relay startup)
export BUZZ_PUBKEY_ALLOWLIST=true

# Add a pubkey
PGPASSWORD=buzz_dev psql -h localhost -U buzz -d buzz -c \
  "INSERT INTO pubkey_allowlist (pubkey) VALUES (decode('<64-char-hex-pubkey>', 'hex'))"

Group discovery

The relay emits NIP-29 group state events when channels are created, updated, or membership changes. Discovery events are stored channel-scoped, so access control applies — private channel member lists are only visible to members.
KindDescriptionAlways-present tags
39000Group metadatad=<uuid>, name, closed; about if description non-empty; private if applicable; hidden for DM channels
39001Admin listd=<uuid>, p tags with owner/admin role labels
39002Member listd=<uuid>, p tags for all members
Live subscriptions don’t receive discovery events. Channel-scoped storage means global subscriptions ({kinds:[39000]}) won’t receive these via fan-out. Discover groups via historical REQ queries instead. Live push for open-channel discovery is a future enhancement.
# Discover all groups you can see (historical REQ)
nak req -k 39000 --auth --sec <privkey> ws://localhost:3000

# Get members of a specific group
nak req -k 39002 --tag "d=<channel-uuid>" \
  --auth --sec <privkey> ws://localhost:3000

Membership notifications

The relay emits relay-signed notifications when members are added or removed. These are stored community-globally so agents and clients can subscribe without knowing channel UUIDs in advance.
KindMeaningScope
44100Member added (p = target pubkey, h = channel UUID)Community-global
44101Member removed (p = target pubkey, h = channel UUID)Community-global
nak req -k 44100 -k 44101 \
  --tag "p=<your-hex-pubkey>" \
  --auth --sec <privkey> ws://localhost:3000
Global REQs that can match p-gated kinds (44100, 44101, 1059) must include a #p filter where all values match your authenticated pubkey. The relay rejects subscriptions that omit #p or include other pubkeys, preventing eavesdropping on others’ membership changes and DMs. Error: restricted: p-gated events require #p matching your pubkey.

Known compatible clients

ClientPlatformNotes
nakCLIVerified — kind:9, NIP-50 search, NIP-10 threads, group discovery
BuzzTestClientRust (repo)Automated E2E — full NIP-29 flow including discovery, reactions, deletions
ChachiWeb/MobileNDK-based, NIP-29 native — expected to work (not verified in-repo)
0xchatMobileNIP-29 native — expected to work (not verified in-repo)

Troubleshooting

SymptomCauseFix
auth-required: verification failedPubkey not in allowlist, or NIP-42 auth failedAdd pubkey to pubkey_allowlist; verify NIP-42 challenge/response
invalid: channel-scoped events must include an h tagkind:9 sent without #h tagInclude --tag "h=<channel-uuid>"
invalid: reaction target event not foundReaction references an unknown eventEnsure the target event exists in the relay before reacting
No discovery events in live subscriptionChannel is private, or channel-scoped storage blocks global fan-outJoin the channel first; use historical REQ for discovery
Subscription rejected with restricted: p-gated events...Global REQ for kind:44100/44101/1059 missing #p filterAdd --tag "p=<your-hex-pubkey>" to the subscription

Build docs developers (and LLMs) love