go-nostr ======== A set of useful things for [Nostr Protocol](https://github.com/nostr-protocol/nostr) implementations. GoDoc
[![test every commit](https://github.com/nbd-wtf/go-nostr/actions/workflows/test.yml/badge.svg)](https://github.com/nbd-wtf/go-nostr/actions/workflows/test.yml) Install go-nostr: ```bash go get github.com/nbd-wtf/go-nostr ``` ### Generating a key ``` go package main import ( "fmt" "github.com/nbd-wtf/go-nostr" "github.com/nbd-wtf/go-nostr/nip19" ) func main() { sk := nostr.GeneratePrivateKey() pk, _ := nostr.GetPublicKey(sk) nsec, _ := nip19.EncodePrivateKey(sk) npub, _ := nip19.EncodePublicKey(pk) fmt.Println("sk:", sk) fmt.Println("pk:", pk) fmt.Println(nsec) fmt.Println(npub) } ``` ### Subscribing to a single relay ``` go 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 ``` go 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. ``` go 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 ```