Documentation
¶
Index ¶
- Variables
- func ApplyBudget(budget int, filters ...nostr.Filter)
- func DisconnectOnDrops(maxDropped int) func(c Client)
- func HandleSignals(cancel context.CancelFunc)
- func IP(r *http.Request) string
- func InvalidID(c Client, e *nostr.Event) error
- func InvalidSignature(c Client, e *nostr.Event) error
- func RegistrationFailWithin(d time.Duration) func(Stats, *http.Request) error
- type Client
- type Hooks
- type OnHooks
- type Option
- func WithClientResponseLimit(n int) Option
- func WithDomain(d string) Option
- func WithInfo(info nip11.RelayInformationDocument) Option
- func WithLogger(l *slog.Logger) Option
- func WithMaxMessageSize(s int64) Option
- func WithMaxProcessors(n int) Option
- func WithPingPeriod(d time.Duration) Option
- func WithPongWait(d time.Duration) Option
- func WithQueueCapacity(c int) Option
- func WithReadBufferSize(s int) Option
- func WithWriteBufferSize(s int) Option
- func WithWriteWait(d time.Duration) Option
- type RejectHooks
- type Relay
- func (r *Relay) Broadcast(e *nostr.Event) error
- func (r *Relay) Clients() int
- func (r *Relay) Filters() int
- func (r *Relay) LastRegistrationFail() time.Time
- func (r *Relay) PrintStats()
- func (r *Relay) QueueLoad() float64
- func (r *Relay) ServeHTTP(w http.ResponseWriter, req *http.Request)
- func (r *Relay) ServeNIP11(w http.ResponseWriter)
- func (r *Relay) ServeWS(w http.ResponseWriter, req *http.Request)
- func (r *Relay) Start(ctx context.Context)
- func (r *Relay) StartAndServe(ctx context.Context, address string) error
- func (r *Relay) Subscriptions() int
- func (r *Relay) TotalConnections() int
- func (r *Relay) Wait()
- type Stats
- type Subscription
- type WhenHooks
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidAuthRequest = errors.New(`an AUTH request must follow this format: ['AUTH', {event_JSON}]`) ErrInvalidTimestamp = errors.New(`created_at must be within one minute from the current time`) ErrInvalidAuthKind = errors.New(`invalid AUTH kind`) ErrInvalidAuthChallenge = errors.New(`invalid AUTH challenge`) ErrInvalidAuthRelay = errors.New(`invalid AUTH relay`) )
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") )
var ( ErrGeneric = errors.New(`the request must be a JSON array`) ErrUnsupportedType = errors.New(`the request type must be one between 'EVENT', 'REQ', 'CLOSE', 'COUNT' and 'AUTH'`) ErrInvalidEventRequest = errors.New(`an EVENT request must follow this format: ['EVENT', {event_JSON}]`) ErrInvalidEventID = errors.New(`invalid event ID`) ErrInvalidEventSignature = errors.New(`invalid event signature`) ErrInvalidReqRequest = errors.New(`a REQ request must follow this format: ['REQ', {subscription_id}, {filter1}, {filter2}, ...]`) ErrInvalidCountRequest = errors.New(`a COUNT request must follow this format: ['COUNT', {subscription_id}, {filter1}, {filter2}, ...]`) ErrInvalidSubscriptionID = errors.New(`invalid subscription ID`) )
Functions ¶
func ApplyBudget ¶
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 ¶
DisconnectOnDrops returns a When.GreedyClient function that sends a notice and disconnects the client if it dropped more than the maximum responses.
func HandleSignals ¶
func HandleSignals(cancel context.CancelFunc)
HandleSignals listens to os signals, and then fires the cancel() function. This cancels the associated context, propagating the signal to the rest of the program.
func InvalidSignature ¶
InvalidSignature returns an error if the event's signature is invalid.
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.
IP() string
// Pubkey the client used to authenticate with NIP-42, or an empty string if it didn't.
// To initiate the authentication, call [Client.SendAuth].
Pubkey() string
// 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)
// 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()
// 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 ¶
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 ¶
func DefaultHooks() Hooks
type OnHooks ¶
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(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(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(Client)
// Event defines how the relay processes an EVENT, for example by storing it in a database.
Event func(Client, *nostr.Event) error
// Req defines how the relay processes a REQ 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(context.Context, Client, nostr.Filters) ([]nostr.Event, error)
// Count defines how the relay processes NIP-45 COUNT requests.
// This hook is optional (= nil). If unset, COUNT requests are rejected with [ErrUnsupportedNIP45].
Count func(Client, 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 ¶
func DefaultOnHooks() OnHooks
type Option ¶
type Option func(*Relay)
func WithClientResponseLimit ¶
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 WithDomain ¶
WithDomain sets the relay's official domain name (e.g., "example.com"). This is mandatory for validating NIP-42 authentication. If this is unset, NIP-42 authentication will fail, and a warning will be logged.
func WithInfo ¶
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 ¶
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 WithMaxMessageSize ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
WithReadBufferSize sets the read buffer size (in bytes) for the underlying websocket connection upgrader.
func WithWriteBufferSize ¶
WithWriteBufferSize sets the write buffer size (in bytes) for the underlying websocket connection upgrader.
func WithWriteWait ¶
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.
type RejectHooks ¶
type RejectHooks struct {
// Connection is invoked before establishing a new client connection.
// Returning a non-nil error rejects the connection.
Connection []func(Stats, *http.Request) error
// Event is invoked before processing an EVENT message.
// Returning a non-nil error rejects the event.
Event []func(Client, *nostr.Event) error
// Req is invoked before processing a REQ message.
// Returning a non-nil error rejects the request.
Req []func(Client, nostr.Filters) error
// Count is invoked before processing a NIP-45 COUNT request.
// Returning a non-nil error rejects the request.
Count []func(Client, 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 ¶
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 ¶
NewRelay creates a new Relay instance with sane defaults and customizable internal behavior. Customize its structure with functional options (e.g., WithDomain, WithQueueCapacity). Customize its behaviour by defining On.Event, On.Req and other Hooks.
Example:
relay := NewRelay(
WithDomain("example.com"), // required for proper NIP-42 validation
WithQueueCapacity(5000),
WithPingPeriod(30 * time.Second),
)
func (*Relay) LastRegistrationFail ¶
func (*Relay) PrintStats ¶
func (r *Relay) PrintStats()
Print important stats of the relay while it's running.
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 ¶
func (r *Relay) ServeNIP11(w http.ResponseWriter)
ServeNIP11 serves the NIP-11 relay information document.
func (*Relay) ServeWS ¶
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 ¶
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 ¶
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) Subscriptions ¶
func (*Relay) TotalConnections ¶
func (*Relay) Wait ¶
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 ¶
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.
//
// Warning:
// Calling methods that lead to sending a response (e.g., [Client.SendAuth]
// or [Client.SendNotice]) from within this hook is highly discouraged.
//
// If a send method is called here, and the response buffer remains full,
// it will trigger this hook again, leading to uncontrolled recursion.
//
// To prevent crashes, any logic that calls a send method must include a
// terminal condition (e.g., a counter or a flag) to guarantee the recursion will stop.
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 ¶
func DefaultWhenHooks() WhenHooks
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
nostr-relay
command
|
|
|
examples
|
|
|
anti-crawlers
command
|
|
|
auth
command
|
|
|
basic
command
|
|
|
blacklist
command
|
|
|
clickhouse
command
|
|
|
count
command
|
|
|
dvm
command
|
|
|
ip-rate
command
|
|
|
logger
command
|
|
|
nip11
command
|
|
|
sparing
command
|
|
|
wot
command
|
|
|
storage
|
|
