rely

package module
v1.5.2 Latest Latest
Warning

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

Go to latest
Published: May 20, 2026 License: MIT Imports: 21 Imported by: 3

README

rely

A framework for building super custom Nostr relays you can rely on. Written in Go, it's designed to be simple and performant, while providing an exeptional developer experience.

Go Reference Go Report Card

Installation

go get github.com/pippellia-btc/rely

Simple and Customizable

Getting started is easy, and deep customization is just as straightforward.

package main

import (
	"context"
	"os/signal"
	"syscall"

	"github.com/pippellia-btc/rely"
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
	defer cancel()

	relay := rely.NewRelay()

	err := relay.StartAndServe(ctx, "localhost:3334")
	if err != nil {
		panic(err)
	}
}
Structural Customization

Fine-tune core parameters using functional options:

relay := rely.NewRelay(
	rely.WithAuthURL("myDomain.com"),	// required for NIP-42 validation
	rely.WithLogger(myLogger),			// configure the relay logger
	rely.WithInfo(myRelayInfo)			// set up nip-11 information document
)

To find all the available options and documentation, see options.go.

Behavioral Customization

You are not limited to simple configuration variables. The relay architecture facilitates complete behavioral customization by allowing you to inject your own functions into its Hooks. This gives you full control over the connection lifecycle, event flow and rate-limiting, enabling any custom business logic.

Below is a silly example that illustrates rely's flexibility.

func main() {
	// ...
	relay.Reject.Connection.Append(BadIP)
	relay.Reject.Event.Append(RejectSatan)
	relay.On.Event = Save	// your custom DB save
}

func BadIP(s Stats, req *http.Request) error {
	if slices.Contains(blacklist, rely.GetIP(req).Group()) {
		return fmt.Errorf("you shall not pass!")
	}
	return nil
}

func RejectSatan(client Client, event *nostr.Event) error {
	if event.Kind == 666 {
		blacklist = append(blacklist, client.IP().Group())
		client.Disconnect()
		return errors.New("not today, Satan. Not today")
	}
	return nil
}

You can find all the available hooks and documentation, in hooks.go.
If you need additional hooks, don't hesitate to open an issue!

If you are looking for inspiration or examples, check out the examples directory.

Intuitive interfaces

If you've read the NIP-01, you already know how to use rely.
Don't believe it? These three are the only interfaces that you'll have to deal with:

No obscure structures leaking implementation details, just intuitive methods that almost require no explanation. As an example, these are some of the methods of the Client interface (all safe for concurrent use).

type Client interface {
	IP() IP
	
	Pubkeys() []string

	ConnectedAt() time.Time

	Subscriptions() []Subscription

	SendNotice(msg string)

	SendAuth()

	Disconnect()

	// All other methods...
	// (don't fear, there are be comments and docs for all methods)
}

Performant

Rely is completely written in Go, a memory-safe and low level language that is easy to learn and performant. It's particularly fit for building server-side applications thanks to its powerful concurrency primitives like channels and goroutines.

These features are extensively used in rely, creating a lock-free architecture on the hot-paths.
Inspired by strfry, rely also implements inverted indexes for matching broadcasted events with subscriptions, making this crucial operation very efficient. For more, check out the architecture section.

All of these optimizations allow a dummy implementation using rely to serve 8000+ concurrent "spammy" clients with less than 1GB of RAM on a 2017 i5 CPU (4-cores).

Secure by Design

To prevent resource abuse, each client is assigned a fixed-size queue for outgoing messages.
Before processing each REQ, the relay calculates the remaining free space and uses that number as a hard cap for the filters' limits. If the total request exceeds the budget, the larger filters are scaled down proportionally.

Here is an example illustrating it.

1. client's queue has a free capacity of 300.

2. client sends a REQ with two filters f1 and f2.
	f1: limit=10		f2: no limit

3. the filters' limits are modified to match the free capacity:
	f1: limit=10		f2: limit=290

This prevents waste of CPU and bandwidth on events the client will not see, and penalizes clients that request more than they consume. The size of the client's queue can be customized with the appropriate option.

Architecture

Client

The Client is the middleman between the websocket connection and the Relay. It reads and writes to the websocket in two separated goroutines, ensuring proper serialization.

Client.read:

  • reads from the websocket and parses nostr messages
  • applies the user defined Reject hooks
  • sends to the Processor's queue
  • handles NIP-42 authentication

Client.write:

  • receives responses in a dedicated queue
  • writes them to the websocket
Processor

The Processor received incoming requests (REQs, EVENTs, COUNTs), and handles them by applying the user defined On hooks (e.g. On.Event, On.Req, On.Count). It consumes from a dedicated queue with a limited but configurable number of worker goroutines.

Dispatcher

The Dispatcher is responsible for broadcasting newly received events to all matching subscriptions. To accomplish this task efficiently, it maintains inverted indexes for all active subscriptions of all clients, an approach inspired by strfry.

However, the Dispatcher is not the ultimate authority on the subscriptions state. Each client is the authority for its own subscriptions, while the dispatcher only maintains an eventually-consistent snapshot of the active ones. This decision is motivated by two reasons:

  1. having CLOSEs cancel the subscriptions as fast as possible to save work.
  2. making calls to Client.Subscriptions very efficiently.

Well tested

Rely fetures unit tests for components that make sense to test in isolation. More importantly, we have a random stress test where the relay is bombarded with thousands of connections, events, filters, and abrupt disconnections every second. This test alone allowed the discovery of hard concurrency bugs and race conditions impossible to detect with simplistic unit tests.

Used by

This section lists project and repositories that are using rely in production.

Databases

Rely doesn't come with a default database, you have to provide your own.
Fortunately, the community has developed several ready-to-use database implementations:

  • nostr-sqlite: a performant and highly customizable SQLite store for Nostr events.
  • eventstore: A collection of reusable database connectors, wrappers and schemas that store Nostr events

FAQs

Why does On.Req accept multiple filters?

As per NIP-01, a REQ can contain multiple filters. Returning only one filter (at the time) would prevent the user of rely from implementing custom query optimizations.

You can always loop through the filters and make a query for each.

When Negentropy? It's in the roadmap.
When NIP-86?

Does anyone uses NIP-86? If so, please let me know.
Just so you know, you can embedd the Relay inside a http.Server, where you can configure all the API routes you want, as shown in this example

How can I add my custom API?

You can embedd the Relay inside a http.Server, where you can configure all the API routes you want, as shown in this example

Documentation

Index

Constants

View Source
const DefaultIPv6Prefix = 64

Variables

View Source
var (
	ErrShuttingDown     = errors.New("the relay is shutting down, please try again later")
	ErrOverloaded       = errors.New("the relay is overloaded, please try again later")
	ErrUnsupportedNIP45 = errors.New("NIP-45 COUNT is not supported")
)
View Source
var (
	ErrGeneric         = errors.New(`the message must be a JSON array`)
	ErrUnsupportedType = errors.New(`the message type must be one between 'EVENT', 'REQ', 'CLOSE', 'COUNT' and 'AUTH'`)

	ErrInvalidEvent    = errors.New(`an EVENT request must follow this format: ['EVENT', {event_JSON}]`)
	ErrInvalidEventID  = errors.New(`invalid event ID`)
	ErrInvalidEventSig = errors.New(`invalid event signature`)

	ErrInvalidReq       = errors.New(`a REQ request must follow this format: ['REQ', {id}, {filter1}, {filter2}, ...]`)
	ErrInvalidCount     = errors.New(`a COUNT request must follow this format: ['COUNT', {id}, {filter1}, {filter2}, ...]`)
	ErrInvalidRequestID = errors.New(`invalid request ID`)
)

Functions

func ApplyBudget added in v0.9.1

func ApplyBudget(budget int, filters ...nostr.Filter)

ApplyBudget adjusts the Limit of each filter in-place so that the total does not exceed the given budget. Filters with limits <= budget / len(filters) are preserved, while larger ones are scaled down proportionally. It panics if budget is negative.

func DisconnectOnDrops added in v1.0.0

func DisconnectOnDrops(maxDropped int) func(c Client)

DisconnectOnDrops returns a When.GreedyClient function that sends a notice and disconnects the client if it dropped more than the maximum responses.

func InvalidID

func InvalidID(c Client, e *nostr.Event) error

InvalidID returns an error if the event's ID is invalid

func InvalidSignature

func InvalidSignature(c Client, e *nostr.Event) error

InvalidSignature returns an error if the event's signature is invalid.

func RegistrationFailWithin added in v0.9.0

func RegistrationFailWithin(d time.Duration) func(Stats, *http.Request) error

RegistrationFailWithin returns a Reject.Connection function that errs if a client registration has failed within the given duration.

Types

type Client

type Client interface {
	// UID is the unique identified for the client, useful to tie its identity to
	// external statistics or resources.
	UID() string

	// IP address of the client. For rate-limiting purposes you should use [IP.Group]
	// or [IP.GroupPrefix] as a normalized representation of the IP.
	IP() IP

	// Pubkeys return the slice of unique pubkeys the client used to authenticate with NIP-42.
	// To initiate the authentication, call [Client.SendAuth].
	Pubkeys() []string

	// IsAuthed returns whether the client has performed authentication with one
	// or more pubkeys. It's a more efficient version of len(Client.Pubkeys) > 0.
	IsAuthed() bool

	// SendAuth sends the client a newly generated AUTH challenge.
	// This resets the authentication state: any previously authenticated pubkey is cleared,
	// and a new challenge is generated and sent.
	SendAuth()

	// ConnectedAt returns the time when the client connected.
	ConnectedAt() time.Time

	// Age returns how long the client has been connected.
	// Short for time.Since(client.ConnectedAt()).
	Age() time.Duration

	// Subscriptions returns a snapshot of the currently active [Subscription]s of the client.
	Subscriptions() []Subscription

	// SendNotice to the client, useful for greetings, warnings and other informational messages.
	SendNotice(msg string)

	// Disconnect the client, closing its websocket connection with a [websocket.CloseNormalClosure]
	Disconnect()

	// DroppedResponses returns the total number of responses that were dropped
	// because the client’s response channel was full. This value is monotonic
	// and it's useful for implementing backpressure or flow-control strategies.
	DroppedResponses() int

	// RemainingCapacity returns a snapshot of how many slots are currently
	// available in the client's response buffer. Useful for implementing
	// backpressure or flow-control strategies.
	RemainingCapacity() int
}

Client represents the nostr client connected to the relay. All methods are safe for concurrent use.

type Hooks added in v1.0.0

type Hooks struct {
	Reject RejectHooks
	On     OnHooks
	When   WhenHooks
}

Hooks provides a complete set of extension points allowing custom logic to be injected into the relay's lifecycle and operational flow.

These functions are categorized into three groups:

  • Reject: Preemptively blocks incoming data or connections before processing.
  • On: Handles standard lifecycle events (Connect, Disconnect, Auth) and successful data flows (Event, Req, Count).
  • When: Triggers on special, non-standard, or warning conditions.

All functions supplied must be thread-safe and must not be modified at runtime.

func DefaultHooks added in v1.0.0

func DefaultHooks() Hooks

type IP

type IP struct {
	Raw net.IP
}

IP is a wrapper around the standard library net.IP. It provides useful convenience methods such as IP.Group and IP.GroupPrefix for grouping/normalizing IP addresses for rate-limiting purposes.

func GetIP added in v1.2.0

func GetIP(r *http.Request) IP

GetIP returns the IP address of the http request. It parses the extracted IP string into the custom rely.IP wrapper struct.

IMPORTANT: This function assumes the relay is behind a trusted reverse proxy. If this is not the case, clients can easily spoof the IP headers (True-Client-IP, X-Real-IP).

func (IP) Group added in v1.2.0

func (ip IP) Group() string

Group returns a stable value suitable for rate limiting or grouping.

  • IPv4 addresses: the full /32 address is returned.
  • IPv6 addresses: it uses the default /64 mask to return the network prefix, grouping all traffic from the same standard subnet block.

func (IP) GroupPrefix added in v1.2.0

func (ip IP) GroupPrefix(prefix int) string

GroupPrefix returns a stable value suitable for rate limiting or grouping.

  • IPv4 addresses: the full /32 address is returned.
  • IPv6 addresses: it uses the mask with the specified `prefix` (typically 48 or 64), treating all addresses within that network block as a single entity.

It panics if prefix is outside the closed interval [0,128].

func (IP) IsLoopback added in v1.2.0

func (ip IP) IsLoopback() bool

IsLoopback reports whether IP is a loopback address.

func (IP) IsV4 added in v1.2.0

func (ip IP) IsV4() bool

IsV4 returns whether the IP is a valid IPv4 address.

func (IP) IsV6 added in v1.2.0

func (ip IP) IsV6() bool

IsV6 returns whether the IP is an IPv6 address.

func (IP) String added in v1.2.0

func (ip IP) String() string

String returns the string form of the raw net.IP address ip.

type OnHooks added in v1.0.0

type OnHooks struct {
	// Connect runs immediately after a client has been connected and registered.
	// It is guaranteed to run before the Disconnect hook of the same client.
	// This callback must be very fast to avoid blocking the hot path.
	// For longer operations, use goroutines.
	//
	// Example:
	//   relay.On.Connect = func(c Client) {
	//       go longOperation(c)
	//   }
	Connect func(c Client)

	// Disconnect runs immediately after a client has been unregistered and disconnected.
	// It is guaranteed to run after the Connect hook of the same client.
	// This callback must be very fast to avoid blocking the hot path.
	// For longer operations, use goroutines.
	//
	// Example:
	//   relay.On.Disconnect = func(c Client) {
	//       go longOperation(c)
	//   }
	Disconnect func(c Client)

	// Auth is called immediately after a client successfully authenticates.
	// It can be used to load resources tied to the client’s public key or adjust rate limits.
	Auth func(c Client)

	// Event defines how the relay processes an EVENT, for example by storing it in a database.
	Event func(c Client, event *nostr.Event) error

	// Req defines how the relay processes a REQ with the provided id, containing one or more filters,
	// for example by querying the database for matching events.
	// The provided context is canceled if the client sends the corresponding CLOSE message.
	Req func(ctx context.Context, c Client, id string, filters nostr.Filters) ([]nostr.Event, error)

	// Count defines how the relay processes a NIP-45 COUNT request with the provided id,
	// containing one ore more filters.
	// This hook is optional (= nil). If unset, COUNT requests are rejected with [ErrUnsupportedNIP45].
	Count func(c Client, id string, filters nostr.Filters) (count int64, approx bool, err error)
}

OnHooks defines functions invoked after specific relay events occur. These hooks customize how the relay reacts to client actions such as EVENT, REQ, and COUNT messages. Each function is called only after the corresponding input has passed all RejectHooks (if any).

OnHooks are typically used to implement custom processing, persistence, logging, authorization, or other side effects in response to relay activity.

func DefaultOnHooks added in v1.0.0

func DefaultOnHooks() OnHooks

type Option added in v0.4.0

type Option func(*Relay)

func WithAuthChallengeBytes added in v1.5.0

func WithAuthChallengeBytes(n uint8) Option

WithAuthChallengeBytes sets the number of random bytes used to generate AUTH challenges. Must be greater than 0. Default is 16 bytes.

func WithAuthTimeTolerance added in v1.5.0

func WithAuthTimeTolerance(d time.Duration) Option

WithAuthTimeTolerance sets the maximum allowed clock skew when validating AUTH created_at. Must be greater than 0. Default is 1 minute.

func WithAuthURL added in v1.5.0

func WithAuthURL(url string) Option

WithAuthURL sets the relay's canonical URL (host + path) used to validate NIP-42 authentication. The "relay" tag in AUTH events must match this value for authentication to succeed. Scheme (ws/wss) and port are ignored as they are transport details.

Examples: "relay.example.com", "example.com/relay", "wss://example.com/nostr"

This is mandatory for NIP-42 authentication. If unset, all AUTH attempts will fail.

func WithClientResponseLimit added in v0.9.0

func WithClientResponseLimit(n int) Option

WithClientResponseLimit sets the maximum number of responses that can be buffered and sent to a single client connection before backpressure is applied. Must be greater than 0.

For each REQ, the framework dynamically adjusts the "limit" field across all filters to be less than the remaining capacity of the client's response channel:

sum filter's limit <= responseLimit - len(client.responses)

This ensures that the total number of events returned never exceeds what can be buffered and sent to the client, enforcing per-client backpressure and preventing overproduction of responses.

func WithIdleTimeout added in v1.2.2

func WithIdleTimeout(d time.Duration) Option

WithIdleTimeout sets the maximum duration an HTTP connection can be idle before being closed. It's used only in the http server used by Relay.StartAndServe. Must be > 10s.

func WithInfo added in v0.8.0

func WithInfo(info nip11.RelayInformationDocument) Option

WithInfo sets a custom NIP-11 (Relay Information Document) JSON body returned when a request includes `Accept: application/nostr+json`. If not set, a default document is used.

func WithLogger added in v1.0.0

func WithLogger(l *slog.Logger) Option

WithLogger sets the structured logger (*slog.Logger) used by the relay for all logging operations. If not set, a default logger will be used.

func WithMaxClientPubkeys added in v1.1.0

func WithMaxClientPubkeys(n int) Option

WithMaxClientPubkeys sets the maximum number of unique pubkeys with which a client can be authenticated at the same time.

func WithMaxMessageSize added in v0.4.0

func WithMaxMessageSize(s int64) Option

WithMaxMessageSize sets the maximum size (in bytes) of a single incoming websocket message (e.g., a Nostr EVENT or REQ). Messages larger than this will be rejected. Must be > 512 bytes.

func WithMaxProcessors added in v0.7.0

func WithMaxProcessors(n int) Option

WithMaxProcessors sets the maximum number of concurrent workers (goroutines) used to process incoming client requests (EVENTs, REQs, COUNTs) from the internal queue. Must be greater than 0.

func WithPingPeriod added in v0.4.0

func WithPingPeriod(d time.Duration) Option

WithPingPeriod sets the interval at which the relay sends Ping messages to the client to keep the connection alive. Must be less than the pong wait and greater than 1s.

func WithPongWait added in v0.4.0

func WithPongWait(d time.Duration) Option

WithPongWait sets the read deadline for waiting for the next Pong message from the client after a Ping is sent. Must be greater than the ping period.

func WithQueueCapacity added in v0.4.0

func WithQueueCapacity(c int) Option

WithQueueCapacity sets the capacity of the internal channel used to queue incoming requests before they are processed by the worker pool. A larger capacity can help smooth out bursts of client activity.

func WithReadBufferSize added in v0.4.0

func WithReadBufferSize(s int) Option

WithReadBufferSize sets the read buffer size (in bytes) for the underlying websocket connection upgrader.

func WithReadHeaderTimeout added in v1.2.2

func WithReadHeaderTimeout(d time.Duration) Option

WithReadHeaderTimeout sets the maximum duration for reading the headers of an HTTP request. It's used only in the http server used by Relay.StartAndServe. Must be > 1s.

func WithShutdownTimeout added in v1.2.2

func WithShutdownTimeout(d time.Duration) Option

WithShutdownTimeout sets the maximum duration to wait for the HTTP server to gracefully shut down when the context is cancelled. It's used only in the http server used by Relay.StartAndServe.

func WithWriteBufferSize added in v0.4.0

func WithWriteBufferSize(s int) Option

WithWriteBufferSize sets the write buffer size (in bytes) for the underlying websocket connection upgrader.

func WithWriteWait added in v0.4.0

func WithWriteWait(d time.Duration) Option

WithWriteWait sets the maximum duration to wait for a websocket write operation (including control messages) to complete before timing out and closing the connection. Must be greater than 1s.

func WithoutMultiAuth added in v1.2.0

func WithoutMultiAuth() Option

WithoutMultiAuth limits to 1 the maximum number of unique pubkeys with which a client can be authenticated at the same time. It's equivalent to WithMaxClientPubkeys(1).

type RejectHooks added in v1.0.0

type RejectHooks struct {
	// Connection is invoked before establishing a new client connection.
	// Returning a non-nil error rejects the connection.
	Connection slice[func(stats Stats, r *http.Request) error]

	// Event is invoked before processing an EVENT message.
	// Returning a non-nil error rejects the event.
	Event slice[func(c Client, event *nostr.Event) error]

	// Req is invoked before processing a REQ message.
	// Returning a non-nil error rejects the request.
	Req slice[func(c Client, id string, filters nostr.Filters) error]

	// Count is invoked before processing a NIP-45 COUNT request.
	// Returning a non-nil error rejects the request.
	Count slice[func(c Client, id string, filters nostr.Filters) error]
}

RejectHooks defines optional functions that can preemptively reject certain actions before they are processed by the relay.

Each function in a hook slice is evaluated in order. If any function returns a non-nil error, the corresponding input (connection, event, request, or count) is immediately rejected.

These hooks are useful for enforcing access policies, validating input, or applying rate limits before the relay performs further processing.

func DefaultRejectHooks added in v1.0.0

func DefaultRejectHooks() RejectHooks

type Relay

type Relay struct {
	Hooks
	// contains filtered or unexported fields
}

Relay is the fundamental structure of the rely package, acting as an orchestrator for the other specialized actors in the system. Its main responsabilities are to register and unregister [clients], and route work to the specialized actors like [dispatcher] and [processor].

func NewRelay

func NewRelay(opts ...Option) *Relay

NewRelay creates a new Relay instance with sane defaults and customizable internal behavior. Customize its structure with functional options (e.g., WithAuthURL, WithQueueCapacity). Customize its behaviour by defining On.Event, On.Req and other Hooks.

Example:

relay := NewRelay(
    WithAuthURL("example.com"), // required for proper NIP-42 validation
    WithQueueCapacity(5000),
    WithPingPeriod(30 * time.Second),
)

func (*Relay) Broadcast added in v0.7.0

func (r *Relay) Broadcast(e *nostr.Event) error

Broadcast the event to all clients whose subscriptions match it.

func (*Relay) Clients

func (r *Relay) Clients() int

func (*Relay) Filters added in v1.0.0

func (r *Relay) Filters() int

func (*Relay) LastRegistrationFail

func (r *Relay) LastRegistrationFail() time.Time

func (*Relay) QueueLoad

func (r *Relay) QueueLoad() float64

func (*Relay) ServeHTTP

func (r *Relay) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements the http.Handler interface, handling WebSocket connections and NIP-11 Relay Information Document requests.

func (*Relay) ServeNIP11 added in v0.8.0

func (r *Relay) ServeNIP11(w http.ResponseWriter)

ServeNIP11 serves the NIP-11 relay information document.

func (*Relay) ServeWS added in v0.8.0

func (r *Relay) ServeWS(w http.ResponseWriter, req *http.Request)

ServeWS upgrades the http request to a websocket, creates a [client], and registers it with the Relay.

func (*Relay) Start

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

Start the relay in separate goroutines in a non-blocking fashion.

The relay will later need to be served using http.ListenAndServe or equivalent. See Relay.StartAndServe for an example on how to do it. For a proper shutdown process, you have to call Relay.Wait before closing your program.

func (*Relay) StartAndServe

func (r *Relay) StartAndServe(ctx context.Context, address string) error

StartAndServe starts the relay, listens to the provided address and handles http requests.

It's a blocking operation, that stops only when the context gets cancelled. Use Relay.Start if you don't want to listen and serve right away, but then don't forget to wait for a graceful shutdown with Relay.Wait.

func (*Relay) StatsReport added in v1.5.0

func (r *Relay) StatsReport() string

StatsReport generates a string report of the relay's statistics at the current time.

func (*Relay) Subscriptions added in v1.0.0

func (r *Relay) Subscriptions() int

func (*Relay) TotalConnections added in v1.0.0

func (r *Relay) TotalConnections() int

func (*Relay) Wait added in v1.0.0

func (r *Relay) Wait()

Wait blocks until the relay has shut down completely.

This is useful only when you manually call Relay.Start instead of Relay.StartAndServe and need to wait for a graceful shutdown before the program exits.

type Stats

type Stats interface {
	// Clients returns the number of active clients connected to the relay.
	Clients() int

	// Subscriptions returns the number of active subscriptions.
	Subscriptions() int

	// Filters returns the number of active filters of REQ subscriptions.
	Filters() int

	// QueueLoad returns the ratio of queued requests to total capacity,
	// represented as a float between 0 and 1.
	QueueLoad() float64

	// LastRegistrationFail returns the last time a client failed to be added
	// to the registration queue, which happens during periods of high load.
	LastRegistrationFail() time.Time

	// TotalConnections returns the total number of connections since the relay startup.
	TotalConnections() int
}

Stats exposes relay statistics, useful for monitoring health or rejecting connections during peaks of activity. All methods are safe for concurrent use.

type Subscription

type Subscription interface {
	// UID is the unique subscription identifier that combines the [Client.UID]
	// with the user-provided subscription ID <Client.UID>:<subscription.ID>
	UID() string

	// ID is a unique identifier within the scope of its client.
	ID() string

	// Filters returns the filters of the subscription.
	Filters() nostr.Filters

	// Matches returns whether any of the subscription's filters match the provided event.
	Matches(*nostr.Event) bool

	// CreatedAt returns the time when the subscription was created.
	CreatedAt() time.Time

	// Age returns how long ago the subscription was created.
	// Short for time.Since(subscription.CreatedAt())
	Age() time.Duration

	// Close the subscription, and send the client a CLOSED message with the provided reason
	Close(reason string)
}

Subscription represent the nostr subscription created by a Client with a REQ. All methods are safe for concurrent use.

type WhenHooks added in v1.0.0

type WhenHooks struct {
	// GreedyClient is invoked when a client’s response buffer becomes full,
	// typically because it sends new REQs before reading responses from earlier ones.
	// This hook is commonly used for logging misbehavior or disconnecting the client.
	GreedyClient func(Client)
}

WhenHooks defines functions invoked when special, non-standard, or exceptional conditions occur during the relay’s operation.

These hooks are useful for detecting and responding to client misbehavior or non-critical performance issues that fall outside the normal operational flow.

func DefaultWhenHooks added in v1.0.0

func DefaultWhenHooks() WhenHooks

Directories

Path Synopsis
Package auth implements NIP-42 authentication for Nostr relays.
Package auth implements NIP-42 authentication for Nostr relays.
examples
anti-crawlers command
api command
auth command
basic command
blacklist command
count command
dvm command
ip-rate command
logger command
nip11 command
sparing command
wot command
Package twindow provides a time-windowed index for efficiently matching timestamps against subscription filters with time bounds (since/until).
Package twindow provides a time-windowed index for efficiently matching timestamps against subscription filters with time bounds (since/until).

Jump to

Keyboard shortcuts

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