From 23765f756fb0475eef38d9b751dbc551581e61bd Mon Sep 17 00:00:00 2001 From: PeterChrz Date: Tue, 11 Aug 2026 21:58:13 -0400 Subject: [PATCH] fix(buzz): wait for NIP-42 challenge before calling Auth() The go-nostr RelayConnect returns immediately after the WebSocket handshake, but the relay has not yet sent the AUTH challenge. Calling relay.Auth() right away signs the auth event with an empty challenge tag, which the Buzz relay rejects permanently ('verification failed' then 'authentication already failed' on retries). Add a 2s sleep between RelayConnect and Auth() to give the background read loop time to receive and store the challenge. The relay typically delivers it within a few hundred milliseconds. --- pkg/channels/buzz/buzz.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pkg/channels/buzz/buzz.go b/pkg/channels/buzz/buzz.go index 2510495c..370f6d2c 100644 --- a/pkg/channels/buzz/buzz.go +++ b/pkg/channels/buzz/buzz.go @@ -10,6 +10,7 @@ import ( "fmt" "strings" "sync" + "time" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip19" @@ -117,9 +118,18 @@ func (c *BuzzChannel) Start(ctx context.Context) error { } c.relay = relay - // NIP-42: the relay challenges, we sign the auth event with the bot identity. - // Buzz relays reject subscriptions from unauthenticated clients, so a failure - // here is fatal rather than advisory. + // NIP-42: the relay sends an AUTH challenge asynchronously after the + // WebSocket handshake completes. go-nostr stores the challenge in an + // unexported field populated by the background read loop. If we call + // Auth() before the challenge arrives, the auth event carries an empty + // challenge tag and the relay rejects it permanently ("verification + // failed" → "authentication already failed" on all subsequent attempts). + // + // We cannot inspect the challenge field directly (unexported), so we wait + // briefly to give the read loop time to receive and store it. The relay + // typically delivers the challenge within a few hundred milliseconds. + time.Sleep(2 * time.Second) + if err := relay.Auth(c.ctx, func(evt *nostr.Event) error { return evt.Sign(c.secretKey) }); err != nil {