nostr

package module
v0.0.0-...-39d91ff Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2026 License: MIT Imports: 36 Imported by: 0

README

This repository is in maintenance mode and adventurous programmers are encouraged to try fiatjaf.com/nostr@master instead.

The core codebase and functionality are the same, but the API breaks a little bit (for good reason) and there are some new features.


Run Tests Go Reference Go Report Card

go-nostr

A set of useful things for Nostr-related software.

go get github.com/getAlby/go-nostr
Generating a key
package main

import (
    "fmt"

    "github.com/getAlby/go-nostr"
    "github.com/getAlby/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
ctx := context.Background()
relay, err := nostr.RelayConnect(ctx, "wss://relay.stoner.com")
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{nostr.KindTextNote},
		Authors: []string{pub},
		Limit:   1,
	}}
} else {
	panic(err)
}

ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()

sub, err := relay.Subscribe(ctx, filters)
if err != nil {
	panic(err)
}

for ev := range sub.Events {
	// handle returned event.
	// channel will stay open until the ctx is cancelled (in this case, context timeout)
	fmt.Println(ev.ID)
}
Publishing to two relays
sk := nostr.GeneratePrivateKey()
pub, _ := nostr.GetPublicKey(sk)

ev := nostr.Event{
	PubKey:    pub,
	CreatedAt: nostr.Now(),
	Kind:      nostr.KindTextNote,
	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
ctx := context.Background()
for _, url := range []string{"wss://relay.stoner.com", "wss://nostr-pub.wellorder.net"} {
	relay, err := nostr.RelayConnect(ctx, url)
	if err != nil {
		fmt.Println(err)
		continue
	}
	if err := relay.Publish(ctx, ev); err != nil {
		fmt.Println(err)
		continue
	}

	fmt.Printf("published to %s\n", url)
}
Logging

To get more logs from the interaction with relays printed to STDOUT you can compile or run your program with -tags debug.

To remove the info logs completely, replace nostr.InfoLogger with something that prints nothing, like

nostr.InfoLogger = log.New(io.Discard, "", 0)
Example script
go run example/example.go
Using libsecp256k1

libsecp256k1 is very fast:

goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz
BenchmarkWithoutLibsecp256k1/sign-4          	    2794	    434114 ns/op
BenchmarkWithoutLibsecp256k1/check-4         	    4352	    297416 ns/op
BenchmarkWithLibsecp256k1/sign-4             	   12559	     94607 ns/op
BenchmarkWithLibsecp256k1/check-4            	   13761	     84595 ns/op
PASS

But to use it you need the host to have it installed as a shared library and CGO to be supported, so we don't compile against it by default.

To use it, use -tags=libsecp256k1 whenever you're compiling your program that uses this library.

Test for Wasm

Install wasmbrowsertest, then run tests:

GOOS=js GOARCH=wasm go test -short ./...

Warning: risk of goroutine bloat (if used incorrectly)

Remember to cancel subscriptions, either by calling .Unsub() on them or ensuring their context.Context will be canceled at some point. If you don't do that they will keep creating a new goroutine for every new event that arrives and if you have stopped listening on the sub.Events channel that will cause chaos and doom in your program.

Contributing to this repository

Use NIP-34 to send your patches to naddr1qqyxwmeddehhxarjqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsz9nhwden5te0wfjkccte9ehx7um5wghxyctwvsq3vamnwvaz7tmjv4kxz7fwwpexjmtpdshxuet5qgsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8grqsqqqaueuwmljc.

Documentation

Index

Constants

View Source
const (
	KindProfileMetadata          int = 0
	KindTextNote                 int = 1
	KindRecommendServer          int = 2
	KindFollowList               int = 3
	KindEncryptedDirectMessage   int = 4
	KindDeletion                 int = 5
	KindRepost                   int = 6
	KindReaction                 int = 7
	KindBadgeAward               int = 8
	KindSimpleGroupChatMessage   int = 9
	KindSimpleGroupThreadedReply int = 10
	KindSimpleGroupThread        int = 11
	KindSimpleGroupReply         int = 12
	KindSeal                     int = 13
	KindDirectMessage            int = 14
	KindGenericRepost            int = 16
	KindReactionToWebsite        int = 17
	KindChannelCreation          int = 40
	KindChannelMetadata          int = 41
	KindChannelMessage           int = 42
	KindChannelHideMessage       int = 43
	KindChannelMuteUser          int = 44
	KindChess                    int = 64
	KindMergeRequests            int = 818
	KindComment                  int = 1111
	KindBid                      int = 1021
	KindBidConfirmation          int = 1022
	KindOpenTimestamps           int = 1040
	KindGiftWrap                 int = 1059
	KindFileMetadata             int = 1063
	KindLiveChatMessage          int = 1311
	KindPatch                    int = 1617
	KindIssue                    int = 1621
	KindReply                    int = 1622
	KindStatusOpen               int = 1630
	KindStatusApplied            int = 1631
	KindStatusClosed             int = 1632
	KindStatusDraft              int = 1633
	KindProblemTracker           int = 1971
	KindReporting                int = 1984
	KindLabel                    int = 1985
	KindRelayReviews             int = 1986
	KindAIEmbeddings             int = 1987
	KindTorrent                  int = 2003
	KindTorrentComment           int = 2004
	KindCoinjoinPool             int = 2022
	KindCommunityPostApproval    int = 4550
	KindJobFeedback              int = 7000
	KindSimpleGroupPutUser       int = 9000
	KindSimpleGroupRemoveUser    int = 9001
	KindSimpleGroupEditMetadata  int = 9002
	KindSimpleGroupDeleteEvent   int = 9005
	KindSimpleGroupCreateGroup   int = 9007
	KindSimpleGroupDeleteGroup   int = 9008
	KindSimpleGroupCreateInvite  int = 9009
	KindSimpleGroupJoinRequest   int = 9021
	KindSimpleGroupLeaveRequest  int = 9022
	KindZapGoal                  int = 9041
	KindNutZap                   int = 9321
	KindTidalLogin               int = 9467
	KindZapRequest               int = 9734
	KindZap                      int = 9735
	KindHighlights               int = 9802
	KindMuteList                 int = 10000
	KindPinList                  int = 10001
	KindRelayListMetadata        int = 10002
	KindBookmarkList             int = 10003
	KindCommunityList            int = 10004
	KindPublicChatList           int = 10005
	KindBlockedRelayList         int = 10006
	KindSearchRelayList          int = 10007
	KindSimpleGroupList          int = 10009
	KindInterestList             int = 10015
	KindNutZapInfo               int = 10019
	KindEmojiList                int = 10030
	KindDMRelayList              int = 10050
	KindUserServerList           int = 10063
	KindFileStorageServerList    int = 10096
	KindGoodWikiAuthorList       int = 10101
	KindGoodWikiRelayList        int = 10102
	KindNWCWalletInfo            int = 13194
	KindLightningPubRPC          int = 21000
	KindClientAuthentication     int = 22242
	KindNWCWalletRequest         int = 23194
	KindNWCWalletResponse        int = 23195
	KindNostrConnect             int = 24133
	KindBlobs                    int = 24242
	KindHTTPAuth                 int = 27235
	KindCategorizedPeopleList    int = 30000
	KindCategorizedBookmarksList int = 30001
	KindRelaySets                int = 30002
	KindBookmarkSets             int = 30003
	KindCuratedSets              int = 30004
	KindCuratedVideoSets         int = 30005
	KindMuteSets                 int = 30007
	KindProfileBadges            int = 30008
	KindBadgeDefinition          int = 30009
	KindInterestSets             int = 30015
	KindStallDefinition          int = 30017
	KindProductDefinition        int = 30018
	KindMarketplaceUI            int = 30019
	KindProductSoldAsAuction     int = 30020
	KindArticle                  int = 30023
	KindDraftArticle             int = 30024
	KindEmojiSets                int = 30030
	KindModularArticleHeader     int = 30040
	KindModularArticleContent    int = 30041
	KindReleaseArtifactSets      int = 30063
	KindApplicationSpecificData  int = 30078
	KindLiveEvent                int = 30311
	KindUserStatuses             int = 30315
	KindClassifiedListing        int = 30402
	KindDraftClassifiedListing   int = 30403
	KindRepositoryAnnouncement   int = 30617
	KindRepositoryState          int = 30618
	KindSimpleGroupMetadata      int = 39000
	KindSimpleGroupAdmins        int = 39001
	KindSimpleGroupMembers       int = 39002
	KindSimpleGroupRoles         int = 39003
	KindWikiArticle              int = 30818
	KindRedirects                int = 30819
	KindFeed                     int = 31890
	KindDateCalendarEvent        int = 31922
	KindTimeCalendarEvent        int = 31923
	KindCalendar                 int = 31924
	KindCalendarEventRSVP        int = 31925
	KindHandlerRecommendation    int = 31989
	KindHandlerInformation       int = 31990
	KindVideoEvent               int = 34235
	KindShortVideoEvent          int = 34236
	KindVideoViewEvent           int = 34237
	KindCommunityDefinition      int = 34550
)
View Source
const MAX_LOCKS = 50

Variables

View Source
var (
	// call SetOutput on InfoLogger to enable info logging
	InfoLogger = log.New(io.Discard, "[go-nostr][info] ", log.LstdFlags)

	// call SetOutput on DebugLogger to enable debug logging
	DebugLogger = log.New(io.Discard, "[go-nostr][debug] ", log.LstdFlags)
)
View Source
var UnknownLabel = errors.New("unknown envelope label")

Functions

func CompareEvent

func CompareEvent(a, b Event) int

CompareEvent is meant to to be used with slices.Sort

func CompareEventPtr

func CompareEventPtr(a, b *Event) int

CompareEventPtr is meant to to be used with slices.Sort

func CompareEventPtrReverse

func CompareEventPtrReverse(b, a *Event) int

CompareEventPtrReverse is meant to to be used with slices.Sort

func CompareEventReverse

func CompareEventReverse(b, a Event) int

CompareEventReverse is meant to to be used with slices.Sort

func FilterEqual

func FilterEqual(a Filter, b Filter) bool

func GeneratePrivateKey

func GeneratePrivateKey() string

func GetPublicKey

func GetPublicKey(sk string) (string, error)

func GetTheoreticalLimit

func GetTheoreticalLimit(filter Filter) int

GetTheoreticalLimit gets the maximum number of events that a normal filter would ever return, for example, if there is a number of "ids" in the filter, the theoretical limit will be that number of ids.

It returns -1 if there are no theoretical limits.

The given .Limit present in the filter is ignored.

func IsAddressableKind

func IsAddressableKind(kind int) bool

func IsEphemeralKind

func IsEphemeralKind(kind int) bool

func IsRegularKind

func IsRegularKind(kind int) bool

func IsReplaceableKind

func IsReplaceableKind(kind int) bool

func IsValid32ByteHex

func IsValid32ByteHex(thing string) bool

IsValid32ByteHex checks if a string is a valid 32-byte hex string.

func IsValidPublicKey

func IsValidPublicKey(pk string) bool

func IsValidRelayURL

func IsValidRelayURL(u string) bool

IsValidRelayURL checks if a URL is a valid relay URL (ws:// or wss://).

func NormalizeHTTPURL

func NormalizeHTTPURL(s string) (string, error)

NormalizeHTTPURL does normalization of http(s):// URLs according to rfc3986. Don't use for relay URLs.

func NormalizeOKMessage

func NormalizeOKMessage(reason string, prefix string) string

NormalizeOKMessage takes a string message that is to be sent in an `OK` or `CLOSED` command and prefixes it with "<prefix>: " if it doesn't already have an acceptable prefix.

func NormalizeURL

func NormalizeURL(u string) string

NormalizeURL normalizes the url and replaces http://, https:// schemes with ws://, wss:// and normalizes the path.

func WithPenaltyBox

func WithPenaltyBox() withPenaltyBoxOpt

WithPenaltyBox just sets the penalty box mechanism so relays that fail to connect or that disconnect will be ignored for a while and we won't attempt to connect again.

func WithRelayOptions

func WithRelayOptions(ropts ...RelayOption) withRelayOptionsOpt

WithRelayOptions sets options that will be used on every relay instance created by this pool.

Types

type AuthEnvelope

type AuthEnvelope struct {
	Challenge *string
	Event     Event
}

AuthEnvelope represents an AUTH message.

func (*AuthEnvelope) FromJSON

func (v *AuthEnvelope) FromJSON(data string) error

func (AuthEnvelope) Label

func (_ AuthEnvelope) Label() string

func (AuthEnvelope) MarshalJSON

func (v AuthEnvelope) MarshalJSON() ([]byte, error)

func (AuthEnvelope) String

func (a AuthEnvelope) String() string

type Cipher

type Cipher interface {
	// Encrypt encrypts a plaintext message for a recipient.
	// Returns the encrypted message as a base64-encoded string.
	Encrypt(ctx context.Context, plaintext string, recipientPublicKey string) (base64ciphertext string, err error)

	// Decrypt decrypts a base64-encoded ciphertext from a sender.
	// Returns the decrypted plaintext.
	Decrypt(ctx context.Context, base64ciphertext string, senderPublicKey string) (plaintext string, err error)
}

Cipher is an interface for encrypting and decrypting messages with NIP-44

type CloseEnvelope

type CloseEnvelope string

CloseEnvelope represents a CLOSE message.

func (*CloseEnvelope) FromJSON

func (v *CloseEnvelope) FromJSON(data string) error

func (CloseEnvelope) Label

func (_ CloseEnvelope) Label() string

func (CloseEnvelope) MarshalJSON

func (v CloseEnvelope) MarshalJSON() ([]byte, error)

func (CloseEnvelope) String

func (c CloseEnvelope) String() string

type ClosedEnvelope

type ClosedEnvelope struct {
	SubscriptionID string
	Reason         string
}

ClosedEnvelope represents a CLOSED message.

func (*ClosedEnvelope) FromJSON

func (v *ClosedEnvelope) FromJSON(data string) error

func (ClosedEnvelope) Label

func (_ ClosedEnvelope) Label() string

func (ClosedEnvelope) MarshalJSON

func (v ClosedEnvelope) MarshalJSON() ([]byte, error)

func (ClosedEnvelope) String

func (c ClosedEnvelope) String() string

type Connection

type Connection struct {
	// contains filtered or unexported fields
}

Connection represents a websocket connection to a Nostr relay.

func NewConnection

func NewConnection(ctx context.Context, url string, requestHeader http.Header, tlsConfig *tls.Config) (*Connection, error)

NewConnection creates a new websocket connection to a Nostr relay.

func (*Connection) Close

func (c *Connection) Close() error

Close closes the websocket connection.

func (*Connection) Ping

func (c *Connection) Ping(ctx context.Context) error

Ping sends a ping message to the websocket connection.

func (*Connection) ReadMessage

func (c *Connection) ReadMessage(ctx context.Context, buf io.Writer) error

ReadMessage reads arbitrary bytes from the websocket connection into the provided buffer.

func (*Connection) WriteMessage

func (c *Connection) WriteMessage(ctx context.Context, data []byte) error

WriteMessage writes arbitrary bytes to the websocket connection.

type CountEnvelope

type CountEnvelope struct {
	SubscriptionID string
	Filter
	Count       *int64
	HyperLogLog []byte
}

CountEnvelope represents a COUNT message.

func (*CountEnvelope) FromJSON

func (v *CountEnvelope) FromJSON(data string) error

func (CountEnvelope) Label

func (_ CountEnvelope) Label() string

func (CountEnvelope) MarshalJSON

func (v CountEnvelope) MarshalJSON() ([]byte, error)

func (CountEnvelope) String

func (c CountEnvelope) String() string

type DirectedFilter

type DirectedFilter struct {
	Filter
	Relay string
}

DirectedFilter combines a Filter with a specific relay URL.

type EOSEEnvelope

type EOSEEnvelope string

EOSEEnvelope represents an EOSE (End of Stored Events) message.

func (*EOSEEnvelope) FromJSON

func (v *EOSEEnvelope) FromJSON(data string) error

func (EOSEEnvelope) Label

func (_ EOSEEnvelope) Label() string

func (EOSEEnvelope) MarshalJSON

func (v EOSEEnvelope) MarshalJSON() ([]byte, error)

func (EOSEEnvelope) String

func (e EOSEEnvelope) String() string

type EntityPointer

type EntityPointer struct {
	PublicKey  string   `json:"pubkey"`
	Kind       int      `json:"kind,omitempty"`
	Identifier string   `json:"identifier,omitempty"`
	Relays     []string `json:"relays,omitempty"`
}

EntityPointer represents a pointer to a nostr entity (addressable event).

func EntityPointerFromTag

func EntityPointerFromTag(refTag Tag) (EntityPointer, error)

EntityPointerFromTag creates an EntityPointer from an "a" tag (but it doesn't check if the tag is really "a", it could be anything).

func (EntityPointer) AsFilter

func (ep EntityPointer) AsFilter() Filter

func (EntityPointer) AsTag

func (ep EntityPointer) AsTag() Tag

func (EntityPointer) AsTagReference

func (ep EntityPointer) AsTagReference() string

func (EntityPointer) MatchesEvent

func (ep EntityPointer) MatchesEvent(evt Event) bool

MatchesEvent checks if the pointer matches an event.

type Envelope

type Envelope interface {
	Label() string
	FromJSON(string) error
	MarshalJSON() ([]byte, error)
	String() string
}

Envelope is the interface for all nostr message envelopes.

func ParseMessage deprecated

func ParseMessage(message string) Envelope

Deprecated: use NewMessageParser instead

type Event

type Event struct {
	ID        string
	PubKey    string
	CreatedAt Timestamp
	Kind      int
	Tags      Tags
	Content   string
	Sig       string
}

Event represents a Nostr event.

func (*Event) CheckID

func (evt *Event) CheckID() bool

CheckID checks if the implied ID matches the given ID more efficiently.

func (Event) CheckSignature

func (evt Event) CheckSignature() (bool, error)

CheckSignature checks if the event signature is valid for the given event. It won't look at the ID field, instead it will recompute the id from the entire event body. If the signature is invalid bool will be false and err will be set.

func (*Event) GetID

func (evt *Event) GetID() string

GetID computes the event ID and returns it as a hex string.

func (Event) MarshalEasyJSON

func (v Event) MarshalEasyJSON(w *jwriter.Writer)

MarshalEasyJSON supports easyjson.Marshaler interface

func (Event) MarshalJSON

func (v Event) MarshalJSON() ([]byte, error)

MarshalJSON supports json.Marshaler interface

func (*Event) Serialize

func (evt *Event) Serialize() []byte

Serialize outputs a byte array that can be hashed to produce the canonical event "id".

func (*Event) Sign

func (evt *Event) Sign(secretKey string) error

Sign signs an event with a given privateKey. It sets the event's ID, PubKey, and Sig fields. Returns an error if the private key is invalid or if signing fails.

func (Event) String

func (evt Event) String() string

func (*Event) UnmarshalEasyJSON

func (v *Event) UnmarshalEasyJSON(l *jlexer.Lexer)

UnmarshalEasyJSON supports easyjson.Unmarshaler interface

func (*Event) UnmarshalJSON

func (v *Event) UnmarshalJSON(data []byte) error

UnmarshalJSON supports json.Unmarshaler interface

type EventEnvelope

type EventEnvelope struct {
	SubscriptionID *string
	Event
}

EventEnvelope represents an EVENT message.

func (*EventEnvelope) FromJSON

func (v *EventEnvelope) FromJSON(data string) error

func (EventEnvelope) Label

func (_ EventEnvelope) Label() string

func (EventEnvelope) MarshalJSON

func (v EventEnvelope) MarshalJSON() ([]byte, error)

type EventPointer

type EventPointer struct {
	ID     string   `json:"id"`
	Relays []string `json:"relays,omitempty"`
	Author string   `json:"author,omitempty"`
	Kind   int      `json:"kind,omitempty"`
}

EventPointer represents a pointer to a nostr event.

func EventPointerFromTag

func EventPointerFromTag(refTag Tag) (EventPointer, error)

EventPointerFromTag creates an EventPointer from an "e" tag (but it could be other tag name, it isn't checked).

func (EventPointer) AsFilter

func (ep EventPointer) AsFilter() Filter

func (EventPointer) AsTag

func (ep EventPointer) AsTag() Tag

AsTag converts the pointer to a Tag.

func (EventPointer) AsTagReference

func (ep EventPointer) AsTagReference() string

func (EventPointer) MatchesEvent

func (ep EventPointer) MatchesEvent(evt Event) bool

type Filter

type Filter struct {
	IDs     []string
	Kinds   []int
	Authors []string
	Tags    TagMap
	Since   *Timestamp
	Until   *Timestamp
	Limit   int
	Search  string

	// LimitZero is or must be set when there is a "limit":0 in the filter, and not when "limit" is just omitted
	LimitZero bool `json:"-"`
}

func (Filter) Clone

func (ef Filter) Clone() Filter

func (Filter) MarshalEasyJSON

func (v Filter) MarshalEasyJSON(w *jwriter.Writer)

MarshalEasyJSON supports easyjson.Marshaler interface

func (Filter) MarshalJSON

func (v Filter) MarshalJSON() ([]byte, error)

MarshalJSON supports json.Marshaler interface

func (Filter) Matches

func (ef Filter) Matches(event *Event) bool

func (Filter) MatchesIgnoringTimestampConstraints

func (ef Filter) MatchesIgnoringTimestampConstraints(event *Event) bool

func (Filter) String

func (ef Filter) String() string

func (*Filter) UnmarshalEasyJSON

func (v *Filter) UnmarshalEasyJSON(l *jlexer.Lexer)

UnmarshalEasyJSON supports easyjson.Unmarshaler interface

func (*Filter) UnmarshalJSON

func (v *Filter) UnmarshalJSON(data []byte) error

UnmarshalJSON supports json.Unmarshaler interface

type Filters

type Filters []Filter

func (Filters) Match

func (eff Filters) Match(event *Event) bool

func (Filters) MatchIgnoringTimestampConstraints

func (eff Filters) MatchIgnoringTimestampConstraints(event *Event) bool

func (Filters) String

func (eff Filters) String() string

type Keyer

type Keyer interface {
	// Signer provides event signing capabilities
	Signer

	// Cipher provides encryption and decryption capabilities (NIP-44)
	Cipher
}

Keyer is an interface for signing events and performing cryptographic operations. It abstracts away the details of key management, allowing for different implementations such as in-memory keys, hardware wallets, or remote signing services (bunker).

type MessageParser

type MessageParser interface {
	// ParseMessage parses a message into an Envelope.
	ParseMessage(string) (Envelope, error)
}

func NewMessageParser

func NewMessageParser() MessageParser

type MultiStore

type MultiStore []RelayStore

func (MultiStore) Publish

func (multi MultiStore) Publish(ctx context.Context, event Event) error

func (MultiStore) QueryEvents

func (multi MultiStore) QueryEvents(ctx context.Context, filter Filter) (chan *Event, error)

func (MultiStore) QuerySync

func (multi MultiStore) QuerySync(ctx context.Context, filter Filter) ([]*Event, error)

type NoticeEnvelope

type NoticeEnvelope string

NoticeEnvelope represents a NOTICE message.

func (*NoticeEnvelope) FromJSON

func (v *NoticeEnvelope) FromJSON(data string) error

func (NoticeEnvelope) Label

func (_ NoticeEnvelope) Label() string

func (NoticeEnvelope) MarshalJSON

func (v NoticeEnvelope) MarshalJSON() ([]byte, error)

func (NoticeEnvelope) String

func (n NoticeEnvelope) String() string

type OKEnvelope

type OKEnvelope struct {
	EventID string
	OK      bool
	Reason  string
}

OKEnvelope represents an OK message.

func (*OKEnvelope) FromJSON

func (v *OKEnvelope) FromJSON(data string) error

func (OKEnvelope) Label

func (_ OKEnvelope) Label() string

func (OKEnvelope) MarshalJSON

func (v OKEnvelope) MarshalJSON() ([]byte, error)

func (OKEnvelope) String

func (o OKEnvelope) String() string

type Pointer

type Pointer interface {
	// AsTagReference returns the pointer as a string as it would be seen in the value of a tag (i.e. the tag's second item).
	AsTagReference() string

	// AsTag converts the pointer with all the information available to a tag that can be included in events.
	AsTag() Tag

	// AsFilter converts the pointer to a Filter that can be used to query for it on relays.
	AsFilter() Filter
	MatchesEvent(Event) bool
}

Pointer is an interface for different types of Nostr pointers.

In this context, a "pointer" is a reference to an event or profile potentially including relays and other metadata that might help find it.

type PoolOption

type PoolOption interface {
	ApplyPoolOption(*SimplePool)
}

PoolOption is an interface for options that can be applied to a SimplePool.

type ProfilePointer

type ProfilePointer struct {
	PublicKey string   `json:"pubkey"`
	Relays    []string `json:"relays,omitempty"`
}

ProfilePointer represents a pointer to a Nostr profile.

func ProfilePointerFromTag

func ProfilePointerFromTag(refTag Tag) (ProfilePointer, error)

ProfilePointerFromTag creates a ProfilePointer from a "p" tag (but it doesn't have to be necessarily a "p" tag, could be something else).

func (ProfilePointer) AsFilter

func (ep ProfilePointer) AsFilter() Filter

func (ProfilePointer) AsTag

func (ep ProfilePointer) AsTag() Tag

func (ProfilePointer) AsTagReference

func (ep ProfilePointer) AsTagReference() string

func (ProfilePointer) MatchesEvent

func (ep ProfilePointer) MatchesEvent(_ Event) bool

MatchesEvent checks if the pointer matches an event.

type PublishResult

type PublishResult struct {
	Error    error
	RelayURL string
	Relay    *Relay
}

PublishResult represents the result of publishing an event to a relay.

type Relay

type Relay struct {
	URL string

	Connection    *Connection
	Subscriptions *xsync.MapOf[int64, *Subscription]

	ConnectionError error

	// custom things that aren't often used
	//
	AssumeValid bool // this will skip verifying signatures for events received from this relay
	// contains filtered or unexported fields
}

Relay represents a connection to a Nostr relay.

func NewRelay

func NewRelay(ctx context.Context, url string, opts ...RelayOption) *Relay

NewRelay returns a new relay. It takes a context that, when canceled, will close the relay connection.

func RelayConnect

func RelayConnect(ctx context.Context, url string, opts ...RelayOption) (*Relay, error)

RelayConnect returns a relay object connected to url.

The given subscription is only used during the connection phase. Once successfully connected, cancelling ctx has no effect.

The ongoing relay connection uses a background context. To close the connection, call r.Close(). If you need fine grained long-term connection contexts, use NewRelay() instead.

func (*Relay) Auth

func (r *Relay) Auth(ctx context.Context, sign func(event *Event) error) error

Auth sends an "AUTH" command client->relay as in NIP-42 and waits for an OK response.

You don't have to build the AUTH event yourself, this function takes a function to which the event that must be signed will be passed, so it's only necessary to sign that.

func (*Relay) Close

func (r *Relay) Close() error

Close closes the relay connection.

func (*Relay) Connect

func (r *Relay) Connect(ctx context.Context) error

Connect tries to establish a websocket connection to r.URL. If the context expires before the connection is complete, an error is returned. Once successfully connected, context expiration has no effect: call r.Close to close the connection.

The given context here is only used during the connection phase. The long-living relay connection will be based on the context given to NewRelay().

func (*Relay) ConnectWithTLS

func (r *Relay) ConnectWithTLS(ctx context.Context, tlsConfig *tls.Config) error

ConnectWithTLS is like Connect(), but takes a special tls.Config if you need that.

func (*Relay) Context

func (r *Relay) Context() context.Context

Context retrieves the context that is associated with this relay connection. It will be closed when the relay is disconnected.

func (*Relay) Count

func (r *Relay) Count(
	ctx context.Context,
	filters Filters,
	opts ...SubscriptionOption,
) (int64, []byte, error)

Count sends a "COUNT" command to the relay and returns the count of events matching the filters.

func (*Relay) IsConnected

func (r *Relay) IsConnected() bool

IsConnected returns true if the connection to this relay seems to be active.

func (*Relay) PrepareSubscription

func (r *Relay) PrepareSubscription(ctx context.Context, filters Filters, opts ...SubscriptionOption) *Subscription

PrepareSubscription creates a subscription, but doesn't fire it.

Remember to cancel subscriptions, either by calling `.Unsub()` on them or ensuring their `context.Context` will be canceled at some point. Failure to do that will result in a huge number of halted goroutines being created.

func (*Relay) Publish

func (r *Relay) Publish(ctx context.Context, event Event) error

Publish sends an "EVENT" command to the relay r as in NIP-01 and waits for an OK response.

func (*Relay) QueryEvents

func (r *Relay) QueryEvents(ctx context.Context, filter Filter) (chan *Event, error)

QueryEvents subscribes to events matching the given filter and returns a channel of events.

In most cases it's better to use SimplePool instead of this method.

func (*Relay) QuerySync

func (r *Relay) QuerySync(ctx context.Context, filter Filter) ([]*Event, error)

QuerySync subscribes to events matching the given filter and returns a slice of events. This method blocks until all events are received or the context is canceled.

In most cases it's better to use SimplePool instead of this method.

func (*Relay) String

func (r *Relay) String() string

String just returns the relay URL.

func (*Relay) Subscribe

func (r *Relay) Subscribe(ctx context.Context, filters Filters, opts ...SubscriptionOption) (*Subscription, error)

Subscribe sends a "REQ" command to the relay r as in NIP-01. Events are returned through the channel sub.Events. The subscription is closed when context ctx is cancelled ("CLOSE" in NIP-01).

Remember to cancel subscriptions, either by calling `.Unsub()` on them or ensuring their `context.Context` will be canceled at some point. Failure to do that will result in a huge number of halted goroutines being created.

func (*Relay) Write

func (r *Relay) Write(msg []byte) <-chan error

Write queues an arbitrary message to be sent to the relay.

type RelayEvent

type RelayEvent struct {
	*Event
	Relay *Relay
}

RelayEvent represents an event received from a specific relay.

func (RelayEvent) String

func (ie RelayEvent) String() string

type RelayOption

type RelayOption interface {
	ApplyRelayOption(*Relay)
}

RelayOption is the type of the argument passed when instantiating relay connections.

type RelayStore

type RelayStore interface {
	Publish(context.Context, Event) error
	QueryEvents(context.Context, Filter) (chan *Event, error)
	QuerySync(context.Context, Filter) ([]*Event, error)
}

type ReplaceableKey

type ReplaceableKey struct {
	PubKey string
	D      string
}

type ReqEnvelope

type ReqEnvelope struct {
	SubscriptionID string
	Filters
}

ReqEnvelope represents a REQ message.

func (*ReqEnvelope) FromJSON

func (v *ReqEnvelope) FromJSON(data string) error

func (ReqEnvelope) Label

func (_ ReqEnvelope) Label() string

func (ReqEnvelope) MarshalJSON

func (v ReqEnvelope) MarshalJSON() ([]byte, error)

type Signer

type Signer interface {
	User

	// SignEvent signs the provided event, setting its ID, PubKey, and Sig fields.
	// The context can be used for operations that may require user interaction or
	// network access, such as with remote signers.
	SignEvent(ctx context.Context, evt *Event) error
}

Signer is a User that can also sign events.

type SimplePool

type SimplePool struct {
	Relays  *xsync.MapOf[string, *Relay]
	Context context.Context
	// contains filtered or unexported fields
}

SimplePool manages connections to multiple relays, ensures they are reopened when necessary and not duplicated.

func NewSimplePool

func NewSimplePool(ctx context.Context, opts ...PoolOption) *SimplePool

NewSimplePool creates a new SimplePool with the given context and options.

func (*SimplePool) BatchedSubManyEose

func (pool *SimplePool) BatchedSubManyEose(
	ctx context.Context,
	dfs []DirectedFilter,
	opts ...SubscriptionOption,
) chan RelayEvent

BatchedSubManyEose performs batched subscriptions to multiple relays with different filters.

func (*SimplePool) Close

func (pool *SimplePool) Close(reason string)

Close closes the pool with the given reason.

func (*SimplePool) CountMany

func (pool *SimplePool) CountMany(
	ctx context.Context,
	urls []string,
	filter Filter,
	opts []SubscriptionOption,
) int

CountMany aggregates count results from multiple relays using NIP-45 HyperLogLog

func (*SimplePool) EnsureRelay

func (pool *SimplePool) EnsureRelay(url string) (*Relay, error)

EnsureRelay ensures that a relay connection exists and is active. If the relay is not connected, it attempts to connect.

func (*SimplePool) FetchMany

func (pool *SimplePool) FetchMany(
	ctx context.Context,
	urls []string,
	filter Filter,
	opts ...SubscriptionOption,
) chan RelayEvent

FetchMany opens a subscription, much like SubscribeMany, but it ends as soon as all Relays return an EOSE message.

func (*SimplePool) FetchManyReplaceable

func (pool *SimplePool) FetchManyReplaceable(
	ctx context.Context,
	urls []string,
	filter Filter,
	opts ...SubscriptionOption,
) *xsync.MapOf[ReplaceableKey, *Event]

FetchManyReplaceable is like FetchMany, but deduplicates replaceable and addressable events and returns only the latest for each "d" tag.

func (*SimplePool) PaginatorWithInterval

func (pool *SimplePool) PaginatorWithInterval(
	interval time.Duration,
) func(ctx context.Context, urls []string, filter Filter, opts ...SubscriptionOption) chan RelayEvent

func (*SimplePool) PublishMany

func (pool *SimplePool) PublishMany(ctx context.Context, urls []string, evt Event) chan PublishResult

PublishMany publishes an event to multiple relays and returns a channel of results emitted as they're received.

func (*SimplePool) QuerySingle

func (pool *SimplePool) QuerySingle(
	ctx context.Context,
	urls []string,
	filter Filter,
	opts ...SubscriptionOption,
) *RelayEvent

QuerySingle returns the first event returned by the first relay, cancels everything else.

func (*SimplePool) SubMany deprecated

func (pool *SimplePool) SubMany(
	ctx context.Context,
	urls []string,
	filters Filters,
	opts ...SubscriptionOption,
) chan RelayEvent

Deprecated: SubMany is deprecated: use SubscribeMany instead.

func (*SimplePool) SubManyEose deprecated

func (pool *SimplePool) SubManyEose(
	ctx context.Context,
	urls []string,
	filters Filters,
	opts ...SubscriptionOption,
) chan RelayEvent

Deprecated: SubManyEose is deprecated: use FetchMany instead.

func (*SimplePool) SubscribeMany

func (pool *SimplePool) SubscribeMany(
	ctx context.Context,
	urls []string,
	filter Filter,
	opts ...SubscriptionOption,
) chan RelayEvent

SubscribeMany opens a subscription with the given filter to multiple relays the subscriptions ends when the context is canceled or when all relays return a CLOSED.

func (*SimplePool) SubscribeManyNotifyEOSE

func (pool *SimplePool) SubscribeManyNotifyEOSE(
	ctx context.Context,
	urls []string,
	filter Filter,
	eoseChan chan struct{},
	opts ...SubscriptionOption,
) chan RelayEvent

SubscribeManyNotifyEOSE is like SubscribeMany, but takes a channel that is closed when all subscriptions have received an EOSE

type Subscription

type Subscription struct {
	Relay   *Relay
	Filters Filters

	// the Events channel emits all EVENTs that come in a Subscription
	// will be closed when the subscription ends
	Events chan *Event

	// the EndOfStoredEvents channel gets closed when an EOSE comes for that subscription
	EndOfStoredEvents chan struct{}

	// the ClosedReason channel emits the reason when a CLOSED message is received
	ClosedReason chan string

	// Context will be .Done() when the subscription ends
	Context context.Context
	// contains filtered or unexported fields
}

Subscription represents a subscription to a relay.

func (*Subscription) Close

func (sub *Subscription) Close()

Close just sends a CLOSE message. You probably want Unsub() instead.

func (*Subscription) Fire

func (sub *Subscription) Fire() error

Fire sends the "REQ" command to the relay.

func (*Subscription) GetID

func (sub *Subscription) GetID() string

GetID returns the subscription ID.

func (*Subscription) Sub

func (sub *Subscription) Sub(_ context.Context, filters Filters)

Sub sets sub.Filters and then calls sub.Fire(ctx). The subscription will be closed if the context expires.

func (*Subscription) Unsub

func (sub *Subscription) Unsub()

Unsub closes the subscription, sending "CLOSE" to relay as in NIP-01. Unsub() also closes the channel sub.Events and makes a new one.

type SubscriptionOption

type SubscriptionOption interface {
	IsSubscriptionOption()
}

SubscriptionOption is the type of the argument passed when instantiating relay connections. Some examples are WithLabel.

type Tag

type Tag []string

func (Tag) Clone

func (tag Tag) Clone() Tag

Clone creates a new array with these tag items inside.

func (Tag) Key deprecated

func (tag Tag) Key() string

Deprecated: write these inline instead

func (Tag) Relay deprecated

func (tag Tag) Relay() string

Deprecated: write these inline instead

func (Tag) StartsWith deprecated

func (tag Tag) StartsWith(prefix []string) bool

Deprecated: this is too cumbersome for no reason when what we actually want is the simpler logic present in Find and FindWithValue.

func (Tag) Value deprecated

func (tag Tag) Value() string

Deprecated: write these inline instead

type TagMap

type TagMap map[string][]string

type Tags

type Tags []Tag

func (Tags) All deprecated

func (tags Tags) All(tagPrefix []string) iter.Seq2[int, Tag]

Deprecated: use FindAll instead

func (Tags) AppendUnique deprecated

func (tags Tags) AppendUnique(tag Tag) Tags

Deprecated: write your own instead with Find() and append()

func (Tags) Clone

func (tags Tags) Clone() Tag

Clone creates a new array with these tags inside.

func (Tags) CloneDeep

func (tags Tags) CloneDeep() Tag

CloneDeep creates a new array with clones of these tags inside.

func (Tags) ContainsAny

func (tags Tags) ContainsAny(tagName string, values []string) bool

func (Tags) FilterOut deprecated

func (tags Tags) FilterOut(tagPrefix []string) Tags

Deprecated: this is useless, write your own

func (*Tags) FilterOutInPlace deprecated

func (tags *Tags) FilterOutInPlace(tagPrefix []string)

Deprecated: this is useless, write your own

func (Tags) Find

func (tags Tags) Find(key string) Tag

Find returns the first tag with the given key/tagName that also has one value (i.e. at least 2 items)

func (Tags) FindAll

func (tags Tags) FindAll(key string) iter.Seq[Tag]

FindAll yields all the tags the given key/tagName that also have one value (i.e. at least 2 items)

func (Tags) FindLast

func (tags Tags) FindLast(key string) Tag

FindLast is like Find, but starts at the end

func (Tags) FindLastWithValue

func (tags Tags) FindLastWithValue(key, value string) Tag

FindLastWithValue is like FindLast, but starts at the end

func (Tags) FindWithValue

func (tags Tags) FindWithValue(key, value string) Tag

FindWithValue is like Find, but also checks if the value (the second item) matches

func (Tags) GetAll deprecated

func (tags Tags) GetAll(tagPrefix []string) Tags

Deprecated: use FindAll instead

func (Tags) GetD

func (tags Tags) GetD() string

GetD gets the first "d" tag (for parameterized replaceable events) value or ""

func (Tags) GetFirst deprecated

func (tags Tags) GetFirst(tagPrefix []string) *Tag

Deprecated: use Find or FindWithValue instead

func (Tags) GetLast deprecated

func (tags Tags) GetLast(tagPrefix []string) *Tag

Deprecated: use FindLast or FindLastWithValue instead

func (*Tags) Scan

func (t *Tags) Scan(src any) error

this exists to satisfy Postgres and stuff and should probably be removed in the future since it's too specific

type Timestamp

type Timestamp int64

func Now

func Now() Timestamp

func (Timestamp) Time

func (t Timestamp) Time() time.Time

type User

type User interface {
	// GetPublicKey returns the public key associated with this user.
	GetPublicKey(ctx context.Context) (string, error)
}

User is an entity that has a public key (although they can't sign anything).

type WithAuthHandler

type WithAuthHandler func(ctx context.Context, authEvent RelayEvent) error

WithAuthHandler must be a function that signs the auth event when called. it will be called whenever any relay in the pool returns a `CLOSED` message with the "auth-required:" prefix, only once for each relay

func (WithAuthHandler) ApplyPoolOption

func (h WithAuthHandler) ApplyPoolOption(pool *SimplePool)

type WithAuthorKindQueryMiddleware

type WithAuthorKindQueryMiddleware func(relay string, pubkey string, kind int)

WithAuthorKindQueryMiddleware is a function that will be called with every combination of relay+pubkey+kind queried in a .SubMany*() call -- when applicable (i.e. when the query contains a pubkey and a kind).

func (WithAuthorKindQueryMiddleware) ApplyPoolOption

func (h WithAuthorKindQueryMiddleware) ApplyPoolOption(pool *SimplePool)

type WithCheckDuplicate

type WithCheckDuplicate func(id, relay string) bool

WithCheckDuplicate sets checkDuplicate on the subscription

func (WithCheckDuplicate) IsSubscriptionOption

func (_ WithCheckDuplicate) IsSubscriptionOption()

type WithCheckDuplicateReplaceable

type WithCheckDuplicateReplaceable func(rk ReplaceableKey, ts Timestamp) bool

WithCheckDuplicateReplaceable sets checkDuplicateReplaceable on the subscription

func (WithCheckDuplicateReplaceable) IsSubscriptionOption

func (_ WithCheckDuplicateReplaceable) IsSubscriptionOption()

type WithCustomHandler

type WithCustomHandler func(data string)

WithCustomHandler must be a function that handles any relay message that couldn't be parsed as a standard envelope.

func (WithCustomHandler) ApplyRelayOption

func (ch WithCustomHandler) ApplyRelayOption(r *Relay)

type WithDuplicateMiddleware

type WithDuplicateMiddleware func(relay string, id string)

WithDuplicateMiddleware is a function that will be called with all duplicate ids received.

func (WithDuplicateMiddleware) ApplyPoolOption

func (h WithDuplicateMiddleware) ApplyPoolOption(pool *SimplePool)

type WithEventMiddleware

type WithEventMiddleware func(RelayEvent)

WithEventMiddleware is a function that will be called with all events received.

func (WithEventMiddleware) ApplyPoolOption

func (h WithEventMiddleware) ApplyPoolOption(pool *SimplePool)

type WithLabel

type WithLabel string

WithLabel puts a label on the subscription (it is prepended to the automatic id) that is sent to relays.

func (WithLabel) IsSubscriptionOption

func (_ WithLabel) IsSubscriptionOption()

type WithNoticeHandler

type WithNoticeHandler func(notice string)

WithNoticeHandler just takes notices and is expected to do something with them. when not given, defaults to logging the notices.

func (WithNoticeHandler) ApplyRelayOption

func (nh WithNoticeHandler) ApplyRelayOption(r *Relay)

type WithRequestHeader

type WithRequestHeader http.Header

WithRequestHeader sets the HTTP request header of the websocket preflight request.

func (WithRequestHeader) ApplyRelayOption

func (ch WithRequestHeader) ApplyRelayOption(r *Relay)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL