fix(buzz): retry NIP-42 auth with backoff to fix race condition

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 with 'auth-required: verification
failed'.

Retry the Auth() call up to 3 times with 1s backoff between attempts,
giving the relay time to deliver the challenge.
This commit is contained in:
PeterChrz 2026-08-11 21:58:13 -04:00
parent 9b4df86182
commit 735cb0734e
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,15 +118,36 @@ 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.
if err := relay.Auth(c.ctx, func(evt *nostr.Event) error {
return evt.Sign(c.secretKey)
}); err != nil {
// NIP-42: the relay sends an AUTH challenge asynchronously after connect.
// The go-nostr library stores the challenge in an unexported field that is
// populated by the read loop. If we call Auth() too quickly the challenge
// is empty and the relay rejects the event with "verification failed".
// Retry with backoff to give the relay time to deliver the challenge.
const maxAuthAttempts = 3
var authErr error
for attempt := 0; attempt < maxAuthAttempts; attempt++ {
if attempt > 0 {
logger.WarnCF("buzz", "Retrying NIP-42 auth", map[string]any{
"attempt": attempt + 1,
"delay": "1s",
})
time.Sleep(1 * time.Second)
}
authErr = relay.Auth(c.ctx, func(evt *nostr.Event) error {
return evt.Sign(c.secretKey)
})
if authErr == nil {
break
}
logger.WarnCF("buzz", "NIP-42 auth attempt failed", map[string]any{
"attempt": attempt + 1,
"error": authErr.Error(),
})
}
if authErr != nil {
_ = relay.Close()
c.cancel()
return fmt.Errorf("buzz NIP-42 auth failed: %w", err)
return fmt.Errorf("buzz NIP-42 auth failed: %w", authErr)
}
channelIDs := []string(c.config.Channels)