NAME

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

SYNOPSIS

use Net::Nostr::Relay;

# Standalone relay (blocks until stop is called)
my $relay = Net::Nostr::Relay->new;
$relay->run('127.0.0.1', 8080);

# Non-blocking: run a relay and client together
use Net::Nostr::Key;
use Net::Nostr::Client;

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

my $key    = Net::Nostr::Key->new;
my $client = Net::Nostr::Client->new;
$client->connect('ws://127.0.0.1:8080');

my $event = $key->create_event(kind => 1, content => 'hello', tags => []);
$client->publish($event);

DESCRIPTION

An in-process Nostr relay. Accepts WebSocket connections, stores events in memory, manages subscriptions, and broadcasts new events to matching subscribers. Events do not persist across restarts.

Implements:

  • NIP-01 - Basic protocol flow

  • NIP-09 - Event deletion requests

  • NIP-42 - Authentication of clients to relays

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 until stop is called. 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. Use this when you want to embed the relay in a larger application, run a client and relay in the same process, or compose with other AnyEvent watchers.

# Run a relay and client together
my $relay = Net::Nostr::Relay->new;
$relay->start('127.0.0.1', 8080);

my $client = Net::Nostr::Client->new;
$client->connect('ws://127.0.0.1:8080');

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. This list is kept in memory only and reflects replaceable/addressable semantics (only the latest version of each replaceable or addressable event is retained). Ephemeral events are never stored here.

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