NAME
Net::Nostr::Message - Nostr protocol message serialization and parsing
SYNOPSIS
use Net::Nostr::Message;
# Client-to-relay: publish an event
my $msg = Net::Nostr::Message->new(type => 'EVENT', event => $event);
my $json = $msg->serialize; # '["EVENT",{...}]'
# Client-to-relay: subscribe
my $msg = Net::Nostr::Message->new(
type => 'REQ',
subscription_id => 'feed',
filters => [$filter1, $filter2],
);
# Client-to-relay: close subscription
my $msg = Net::Nostr::Message->new(
type => 'CLOSE',
subscription_id => 'feed',
);
# Parse a relay EVENT message
my $msg = Net::Nostr::Message->parse($json_string);
say $msg->type; # 'EVENT'
say $msg->subscription_id; # 'feed'
say $msg->event->content; # event content
# Client-to-relay: NIP-42 authentication
my $msg = Net::Nostr::Message->new(type => 'AUTH', event => $auth_event);
# Relay-to-client: AUTH challenge
my $msg = Net::Nostr::Message->parse('["AUTH","challenge-string"]');
say $msg->challenge; # 'challenge-string'
DESCRIPTION
Handles all NIP-01 message types for both client-to-relay and relay-to-client communication. Messages are constructed with new, serialized with serialize, and parsed from JSON with parse.
CONSTRUCTOR
new
my $msg = Net::Nostr::Message->new(type => $type, ...);
Creates a new message. type is required and must be one of: EVENT, REQ, CLOSE, OK, EOSE, NOTICE, CLOSED, COUNT, AUTH, NEG-OPEN, NEG-MSG, NEG-CLOSE, NEG-ERR.
Required fields by type:
EVENT - event (Net::Nostr::Event object), optional subscription_id
REQ - subscription_id, filters (arrayref of Net::Nostr::Filter)
CLOSE - subscription_id
OK - event_id (64-char lowercase hex), accepted (bool), optional message (defaults to '')
EOSE - subscription_id
NOTICE - message (string)
CLOSED - subscription_id, message (string)
COUNT - subscription_id, filters (client-to-relay) or count (non-negative integer, relay-to-client); mutually exclusive
AUTH - challenge (string) or event (Net::Nostr::Event object); mutually exclusive
NEG-OPEN - subscription_id, filter (Net::Nostr::Filter), neg_msg (even-length hex string)
NEG-MSG - subscription_id, neg_msg (even-length hex string)
NEG-CLOSE - subscription_id
NEG-ERR - subscription_id, message (string), optional neg_limit (non-negative integer)
subscription_id must be a non-empty string of at most 64 characters for REQ, CLOSE, and COUNT messages. For EOSE and CLOSED, subscription_id must be defined but is not length-validated (relay-to-client messages echo back whatever the client sent). event_id must be 64-character lowercase hex. event must be a Net::Nostr::Event object. filters must be an arrayref of Net::Nostr::Filter objects. message and challenge must be non-reference scalars.
For AUTH, passing both event and challenge is rejected. For COUNT, passing both count and filters is rejected. Croaks on missing required fields, invalid field formats, mutually exclusive arguments, type mismatches, unknown type, or unknown arguments.
METHODS
serialize
my $json = $msg->serialize;
Returns the JSON-encoded message string per the NIP-01 wire format.
my $msg = Net::Nostr::Message->new(type => 'CLOSE', subscription_id => 'x');
say $msg->serialize; # '["CLOSE","x"]'
parse
my $msg = Net::Nostr::Message->parse($json_string);
Class method. Parses a JSON message string and returns a new Net::Nostr::Message object. Croaks on invalid JSON, unknown message types, or malformed messages. Validates structural format of each message type (element count, field types). String fields (subscription_id, message, challenge) are rejected if they are JSON objects or arrays. event_id in OK messages must be 64-character lowercase hex. For EVENT and AUTH messages, the contained event is constructed via Net::Nostr::Event->from_wire which requires all seven NIP-01 fields (id, pubkey, created_at, kind, tags, content, sig) and rejects missing or undefined fields.
Trust boundary: parse validates message structure and field formats but does not verify event signatures, event ID hashes, or authenticity. The caller is responsible for verifying event integrity via $event->validate, which recomputes the ID hash and verifies the signature against the event's own pubkey.
my $msg = Net::Nostr::Message->parse('["NOTICE","hello"]');
say $msg->type; # 'NOTICE'
say $msg->message; # 'hello'
type
my $type = $msg->type; # 'EVENT', 'OK', 'REQ', etc.
subscription_id
my $sub_id = $msg->subscription_id;
The subscription ID. Present on EVENT (relay-to-client), REQ, CLOSE, EOSE, and CLOSED messages.
event
my $event = $msg->event; # Net::Nostr::Event
The event object. Present on EVENT and AUTH (client-to-relay) messages.
event_id
my $id = $msg->event_id;
The event ID string. Present on OK messages.
accepted
my $bool = $msg->accepted; # 1 or 0
Whether the event was accepted. Present on OK messages.
message
my $text = $msg->message;
The message string. Present on OK, NOTICE, and CLOSED messages. For OK and CLOSED, may include a machine-readable prefix like "duplicate: already have this event".
prefix
my $prefix = $msg->prefix; # 'duplicate', 'blocked', etc. or undef
The machine-readable prefix extracted from the message string. Standard prefixes: duplicate, pow, blocked, rate-limited, invalid, restricted, auth-required, mute, error.
my $msg = Net::Nostr::Message->parse(
'["OK","aa...",false,"blocked: you are banned"]'
);
say $msg->prefix; # 'blocked'
count
my $count = $msg->count; # non-negative integer
The event count. Present on COUNT response messages (NIP-45). Must be a non-negative integer; croaks on references, non-numeric strings, negative values, or floats.
my $msg = Net::Nostr::Message->parse('["COUNT","q1",{"count":42}]');
say $msg->count; # 42
approximate
my $approx = $msg->approximate; # 1 or undef
Whether the count is probabilistic. Present on COUNT response messages when the relay uses approximate counting.
filters
my $filters = $msg->filters; # arrayref of Net::Nostr::Filter
The filter objects. Present on REQ and COUNT (client-to-relay) messages.
challenge
my $challenge = $msg->challenge;
The challenge string. Present on AUTH messages from relays.
my $msg = Net::Nostr::Message->parse('["AUTH","challenge123"]');
say $msg->challenge; # 'challenge123'
neg_msg
my $hex = $msg->neg_msg;
The negentropy protocol message as an even-length hex string. Present on NEG-OPEN and NEG-MSG messages.
filter
my $filter = $msg->filter; # Net::Nostr::Filter
A single NIP-01 filter. Present on NEG-OPEN messages.
neg_limit
my $limit = $msg->neg_limit; # non-negative integer or undef
Optional maximum number of records the relay will process. Present on NEG-ERR messages when the relay rejects a query as too large. Must be a non-negative integer; croaks on references, non-numeric strings, negative values, or floats.
my $msg = Net::Nostr::Message->parse('["NEG-ERR","x","blocked: too big",100000]');
say $msg->neg_limit; # 100000
SEE ALSO
NIP-01, NIP-42, NIP-45, NIP-77, Net::Nostr, Net::Nostr::Event, Net::Nostr::Filter