Documentation
¶
Index ¶
- Constants
- Variables
- func ApplyBudget(budget int, filters ...nostr.Filter)
- func DisconnectOnDrops(maxDropped int) func(c Client)
- 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 IP
- type OnHooks
- type Option
- func WithAuthChallengeBytes(n uint8) Option
- func WithAuthTimeTolerance(d time.Duration) Option
- func WithAuthURL(url string) Option
- func WithClientResponseLimit(n int) Option
- func WithIdleTimeout(d time.Duration) Option
- func WithInfo(info nip11.RelayInformationDocument) Option
- func WithLogger(l *slog.Logger) Option
- func WithMaxClientPubkeys(n int) 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 WithReadHeaderTimeout(d time.Duration) Option
- func WithShutdownTimeout(d time.Duration) Option
- func WithWriteBufferSize(s int) Option
- func WithWriteWait(d time.Duration) Option
- func WithoutMultiAuth() 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) 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) StatsReport() string
- func (r *Relay) Subscriptions() int
- func (r *Relay) TotalConnections() int
- func (r *Relay) Wait()
- type Stats
- type Subscription
- type WhenHooks
Constants ¶
const DefaultIPv6Prefix = 64
Variables ¶
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 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
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
DisconnectOnDrops returns a When.GreedyClient function that sends a notice and disconnects the client if it dropped more than the maximum responses.
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. 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 ¶
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
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
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
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
IsLoopback reports whether IP is a loopback address.
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
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
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
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
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
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
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
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
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
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
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
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
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
WithReadBufferSize sets the read buffer size (in bytes) for the underlying websocket connection upgrader.
func WithReadHeaderTimeout ¶ added in v1.2.2
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
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
WithWriteBufferSize sets the write buffer size (in bytes) for the underlying websocket connection upgrader.
func WithWriteWait ¶ added in v0.4.0
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 ¶
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
Broadcast the event to all clients whose subscriptions match it.
func (*Relay) LastRegistrationFail ¶
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 ¶
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) StatsReport ¶ added in v1.5.0
StatsReport generates a string report of the relay's statistics at the current time.
func (*Relay) Subscriptions ¶ added in v1.0.0
func (*Relay) TotalConnections ¶ added in v1.0.0
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
Source Files
¶
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). |
