NAME
Net::Nostr::Client - WebSocket client for Nostr relays
SYNOPSIS
use Net::Nostr::Client;
use Net::Nostr::Key;
use Net::Nostr::Filter;
my $key = Net::Nostr::Key->new;
my $client = Net::Nostr::Client->new;
# Register callbacks before connecting
$client->on(event => sub {
my ($sub_id, $event) = @_;
say "Got event: " . $event->content;
});
$client->on(ok => sub {
my ($event_id, $accepted, $message) = @_;
say $accepted ? "Accepted" : "Rejected: $message";
});
$client->on(eose => sub {
my ($sub_id) = @_;
say "End of stored events for $sub_id";
});
# Connect (blocks until connected, croaks on failure)
$client->connect("wss://relay.example.com");
# Create and publish an event
my $event = $key->create_event(kind => 1, content => 'hello', tags => []);
$client->publish($event);
# Subscribe with one or more filters
my $filter = Net::Nostr::Filter->new(kinds => [1], limit => 20);
$client->subscribe('my-feed', $filter);
# Close a subscription
$client->close('my-feed');
# Disconnect
$client->disconnect;
DESCRIPTION
A WebSocket client for connecting to Nostr relays. Provides a callback-based interface for publishing events, managing subscriptions, receiving relay messages, counting events (NIP-45), and negentropy set reconciliation (NIP-77). Supports NIP-42 authentication.
CONSTRUCTOR
new
my $client = Net::Nostr::Client->new;
Creates a new client instance. No connection is established until connect is called. Croaks on unknown arguments.
METHODS
connect
$client->connect($url);
# Non-blocking with callback:
$client->connect($url, sub { ... });
Connects to the relay at the given WebSocket URL. Blocks until the connection is established and returns $self for chaining. Croaks if the connection fails, $url is not provided, or the callback is not a CODE ref.
If a callback is provided, connects asynchronously and returns immediately without blocking. The callback receives a single argument: undef on success, or an error string on failure.
$client->connect($url, sub {
my ($err) = @_;
if ($err) {
warn "Connection failed: $err";
return;
}
# connected successfully
});
is_connected
if ($client->is_connected) { ... }
Returns true if the client has an active WebSocket connection.
disconnect
$client->disconnect;
Closes the WebSocket connection. is_connected will return false afterwards. The connection is also automatically cleared if the relay drops the connection.
publish
$client->publish($event);
Sends an EVENT message to the relay. The relay will respond with an OK message (received via the ok callback). Croaks if not connected.
my $key = Net::Nostr::Key->new;
my $event = $key->create_event(kind => 1, content => 'hello', tags => []);
$client->publish($event);
subscribe
$client->subscribe('sub-id', $filter1, $filter2);
Sends a REQ message to the relay with the given subscription ID and filters. The relay will send matching stored events (via event callback), then an EOSE message (via eose callback), then live events as they arrive. Croaks if not connected.
count
$client->count('query-id', $filter1, $filter2);
Sends a COUNT message (NIP-45) to the relay with the given query ID and filters. The relay will respond with a count (received via the count callback). Unlike subscribe, COUNT is one-shot and does not create a live subscription. Croaks if not connected.
$client->on(count => sub {
my ($query_id, $count, $approximate) = @_;
say "Got $count events" . ($approximate ? ' (approximate)' : '');
});
$client->count('followers', Net::Nostr::Filter->new(
kinds => [3], '#p' => [$pubkey],
));
close
$client->close('sub-id');
Sends a CLOSE message to stop receiving events for the given subscription ID. Croaks if not connected.
neg_open
$client->neg_open($sub_id, $filter, $initial_msg);
Sends a NEG-OPEN message (NIP-77) to initiate negentropy set reconciliation. $filter is a Net::Nostr::Filter object, and $initial_msg is the hex-encoded initial message from "initiate" in Net::Nostr::Negentropy. The relay will respond with a neg_msg callback. Croaks if not connected.
Subscription IDs are in a separate namespace from subscribe. If a NEG-OPEN is issued for a currently open subscription ID, the relay closes the existing session first.
use Net::Nostr::Negentropy;
my $ne = Net::Nostr::Negentropy->new;
# add local events via $ne->add_item(...)
$ne->seal;
$client->neg_open('sync1', $filter, $ne->initiate);
neg_msg
$client->neg_msg($sub_id, $msg);
Sends a NEG-MSG message (NIP-77) to continue a negentropy reconciliation round. $msg is the hex-encoded message from "reconcile" in Net::Nostr::Negentropy. Croaks if not connected.
neg_close
$client->neg_close($sub_id);
Sends a NEG-CLOSE message (NIP-77) to terminate a negentropy session and release relay resources. Croaks if not connected.
authenticate
$client->authenticate($key, $relay_url);
Sends a NIP-42 AUTH event to the relay. The $key is a Net::Nostr::Key object used to sign the authentication event, and $relay_url is the relay's URL for the relay tag.
The client must have received an AUTH challenge from the relay first (stored in challenge). Croaks if not connected, if $key or $relay_url is missing, or if no challenge has been received.
$client->on(auth => sub {
my ($challenge) = @_;
$client->authenticate($key, 'wss://relay.example.com/');
});
challenge
my $challenge = $client->challenge;
Returns the most recent AUTH challenge string received from the relay, or undef if no challenge has been received.
on
$client->on($event_type => sub { ... });
Registers a callback for relay messages. The callback must be a CODE ref; croaks otherwise. Only one callback may be registered per event type -- calling on again for the same type replaces the previous callback. Supported event types:
event-sub { my ($subscription_id, $event) = @_; }-
Called for each EVENT message from the relay (both stored and live).
ok-sub { my ($event_id, $accepted, $message) = @_; }-
Called when the relay responds to a published event.
eose-sub { my ($subscription_id) = @_; }-
Called when the relay finishes sending stored events for a subscription.
notice-sub { my ($message) = @_; }-
Called when the relay sends a human-readable NOTICE.
count-sub { my ($query_id, $count, $approximate) = @_; }-
Called when the relay responds to a COUNT request (NIP-45).
$countis the number of matching events,$approximateis true if the count is probabilistic. HyperLogLog (hll) values are not currently parsed. closed-sub { my ($subscription_id, $message) = @_; }-
Called when the relay closes a subscription.
auth-sub { my ($challenge) = @_; }-
Called when the relay sends an AUTH challenge (NIP-42). The client should respond by calling
authenticate. neg_msg-sub { my ($subscription_id, $msg) = @_; }-
Called when the relay sends a NEG-MSG response (NIP-77).
$msgis the hex-encoded negentropy message to pass to "reconcile" in Net::Nostr::Negentropy. neg_err-sub { my ($subscription_id, $message) = @_; }-
Called when the relay sends a NEG-ERR (NIP-77). The negentropy session is considered closed after this.
SEE ALSO
NIP-01, NIP-45, NIP-77, Net::Nostr, Net::Nostr::Event, Net::Nostr::Filter, Net::Nostr::Relay, Net::Nostr::Negentropy