go-nostr
A set of useful things for Nostr Protocol implementations.
Generating a key
sk, _ := nostr.GenerateKey()
pk, _ := nostr.GetPublicKey(sk)
nsec, _ := nip19.EncodePrivateKey(sk)
npub, _ := nip19.EncodePublicKey(pk)
fmt.Println("sk:", sk)
fmt.Println("pk:", nostr.GetPublicKey(sk))
fmt.Println(nsec)
fmt.Println(npub)
Subscribing to a single relay
relay, err := nostr.RelayConnect(context.Background(), "wss://nostr.zebedee.cloud")
if err != nil {
panic(err)
}
npub := "npub1422a7ws4yul24p0pf7cacn7cghqkutdnm35z075vy68ggqpqjcyswn8ekc"
var filters nostr.Filters
if _, v, err := nip19.Decode(npub); err == nil {
pub := v.(string)
filters = []nostr.Filter{{
Kinds: []int{1},
Authors: []string{pub},
Limit: 1,
}}
} else {
panic(err)
}
ctx, cancel := context.WithCancel(context.Background())
sub := relay.Subscribe(ctx, filters)
go func() {
<-sub.EndOfStoredEvents
// handle end of stored events (EOSE, see NIP-15)
}()
for ev := range sub.Events {
// handle returned event.
// channel will stay open until the ctx is cancelled (in this case, by calling cancel())
fmt.Println(ev.ID)
}
Publishing to two relays
sk := nostr.GeneratePrivateKey()
pub, _ := nostr.GetPublicKey(sk)
ev := nostr.Event{
PubKey: pub,
CreatedAt: time.Now(),
Kind: 1,
Tags: nil,
Content: "Hello World!",
}
// calling Sign sets the event ID field and the event Sig field
ev.Sign(sk)
// publish the event to two relays
for _, url := range []string{"wss://nostr.zebedee.cloud", "wss://nostr-pub.wellorder.net"} {
relay, e := nostr.RelayConnect(context.Background(), url)
if e != nil {
fmt.Println(e)
continue
}
fmt.Println("published to ", url, relay.Publish(context.Background(), ev))
}
Authenticating with NIP-42
For this section, the user needs access to a relay implementing NIP-42. E.g., https://github.com/fiatjaf/relayer with a relay implementing the relayer.Auther interface.
func main() {
url := "ws://localhost:7447"
// Once the connection is initiated the server will send "AUTH" with the challenge string.
relay, err := nostr.RelayConnect(context.Background(), url)
if err != nil {
panic(err)
}
// Initialize test user.
sk := nostr.GeneratePrivateKey()
pub, _ := nostr.GetPublicKey(sk)
npub, _ := nip19.EncodePublicKey(pub)
// Relay.Challenges channel will receive the "AUTH" command.
challenge := <-relay.Challenges
// Create the auth event to send back.
// The user will be authenticated as pub.
event := nip42.CreateUnsignedAuthEvent(challenge, pub, url)
event.Sign(sk)
// Set-up context with 3 second time out.
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
// Send the event by calling relay.Auth.
// Returned status is either success, fail, or sent (if no reply given in the 3 second timeout).
auth_status := relay.Auth(ctx, event)
fmt.Printf("authenticated as %s: %s\n", npub, auth_status)
}
Example script
go run example/example.go