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.
This commit is contained in:
PeterChrz 2026-08-11 21:58:13 -04:00
parent 9b4df86182
commit 23765f756f
Signed by untrusted user who does not match committer: pch
GPG key ID: 8F0826ECF7302C63

View file

@ -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 {