NAME

Net::Nostr::Relay - Nostr WebSocket relay server

SYNOPSIS

use Net::Nostr::Relay;

my $relay = Net::Nostr::Relay->new;
$relay->run('127.0.0.1', 8080);  # blocks forever

DESCRIPTION

An in-process Nostr relay implementing NIP-01. Accepts WebSocket connections, stores events, manages subscriptions, and broadcasts new events to matching subscribers.

Supports all NIP-01 event semantics:

  • Regular events - stored and broadcast

  • Replaceable events (kinds 0, 3, 10000-19999) - only latest per pubkey+kind

  • Ephemeral events (kinds 20000-29999) - broadcast but never stored

  • Addressable events (kinds 30000-39999) - only latest per pubkey+kind+d_tag

CONSTRUCTOR

new

my $relay = Net::Nostr::Relay->new;
my $relay = Net::Nostr::Relay->new(verify_signatures => 0);
my $relay = Net::Nostr::Relay->new(max_connections_per_ip => 10);
my $relay = Net::Nostr::Relay->new(relay_url => 'wss://relay.example.com/');

Creates a new relay instance. Options:

verify_signatures - Enable Schnorr signature verification (default: true). Pass 0 to disable (useful for testing with synthetic events).
max_connections_per_ip - Maximum simultaneous WebSocket connections allowed from a single IP address. Connections beyond this limit are rejected at the TCP level. Default: undef (unlimited).
relay_url - The relay's own WebSocket URL (e.g. wss://relay.example.com/). When set, NIP-42 AUTH events are validated to ensure the relay tag matches this URL (domain comparison, case-insensitive). Default: undef (relay tag not validated).

METHODS

run

$relay->run('127.0.0.1', 8080);

Starts the relay and blocks indefinitely, serving connections until stop is called or the process exits. Equivalent to calling start followed by a blocking event loop.

start

$relay->start('127.0.0.1', 8080);

Starts listening for WebSocket connections on the given host and port. Returns immediately without blocking, useful for tests or when you need to run other code alongside the relay.

stop

$relay->stop;

Stops the relay, closes all connections, and clears all subscriptions. If the relay was started with run, also unblocks it. Safe to call on an unstarted relay.

broadcast

$relay->broadcast($event);

Sends the event to all connected clients whose subscriptions match. Normally called internally when a new event is accepted, but can be called directly for testing or custom event injection.

connections

my $conns = $relay->connections;  # hashref

Returns the hashref of active connections.

subscriptions

my $subs = $relay->subscriptions;  # hashref

Returns the hashref of active subscriptions, keyed by connection ID then subscription ID.

events

my $events = $relay->events;  # arrayref of Net::Nostr::Event

Returns the arrayref of stored events.

verify_signatures

my $bool = $relay->verify_signatures;

Returns whether Schnorr signature verification is enabled (default: true).

max_connections_per_ip

my $limit = $relay->max_connections_per_ip;

Returns the maximum number of simultaneous connections allowed per IP address, or undef if unlimited (the default).

my $relay = Net::Nostr::Relay->new(max_connections_per_ip => 10);
$relay->start('0.0.0.0', 8080);

relay_url

my $url = $relay->relay_url;

Returns the relay's own WebSocket URL, or undef if not set. Used for NIP-42 relay tag validation.

my $relay = Net::Nostr::Relay->new(relay_url => 'wss://relay.example.com/');
$relay->start('0.0.0.0', 8080);

authenticated_pubkeys

my $auth = $relay->authenticated_pubkeys;

Returns a hashref of authenticated pubkeys per connection (NIP-42). Keys are connection IDs, values are hashrefs of pubkey hex strings.

my $auth = $relay->authenticated_pubkeys;
for my $conn_id (keys %$auth) {
    for my $pubkey (keys %{$auth->{$conn_id}}) {
        say "Connection $conn_id authenticated as $pubkey";
    }
}

SEE ALSO

Net::Nostr, Net::Nostr::Client, Net::Nostr::Event