NAME
Net::Nostr::RemoteSigning - NIP-46 Nostr Remote Signing
SYNOPSIS
use Net::Nostr::RemoteSigning;
my $remote_signer_pubkey = 'fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52';
my $client_pubkey = 'eff37350d839ce3707332348af4549a96051bd695d3223af4aabce4993531d86';
# Parse a bunker URI (remote-signer initiated)
my $conn = Net::Nostr::RemoteSigning->parse_bunker_uri(
"bunker://${remote_signer_pubkey}?relay=wss%3A%2F%2Frelay.example.com&secret=mysecret"
);
say $conn->remote_signer_pubkey;
say $conn->relays->[0];
# Parse a nostrconnect URI (client initiated)
my $nc = Net::Nostr::RemoteSigning->parse_nostrconnect_uri(
"nostrconnect://${client_pubkey}?relay=wss%3A%2F%2Frelay.example.com&secret=0s8j2djs&name=My+Client"
);
say $nc->client_pubkey;
say $nc->secret;
# Build a request payload
my $payload = Net::Nostr::RemoteSigning->request(
id => 'req-1',
method => 'sign_event',
params => ['{}'],
);
# Build a request event (kind 24133)
my $event = Net::Nostr::RemoteSigning->request_event(
id => 'req-1',
method => 'sign_event',
params => ['{}'],
pubkey => $client_pubkey,
remote_signer_pubkey => $remote_signer_pubkey,
);
# Parse a decrypted response
my $json = JSON->new->utf8->encode({ id => 'req-1', result => 'pong' });
my $resp = Net::Nostr::RemoteSigning->parse_response($json);
if ($resp->is_auth_challenge) {
# Display $resp->auth_url to user
} elsif ($resp->is_error) {
warn $resp->error;
} else {
say $resp->result;
}
# Parse permissions
my @perms = Net::Nostr::RemoteSigning->parse_permissions(
'nip44_encrypt,sign_event:4'
);
DESCRIPTION
Implements NIP-46 Nostr Remote Signing, a protocol for 2-way communication between a remote signer (bunker) and a Nostr client. The remote signer holds the user's private keys and signs events on behalf of the client, minimizing key exposure.
Both request and response events use kind 24133. The content field is encrypted using NIP-44. This module handles the payload structure and event creation; the caller is responsible for encrypting/decrypting the content.
Connection Flow
There are two ways to initiate a connection:
Remote-signer initiated - The signer provides a
bunker://URI. The client sends aconnectrequest to the signer via the specified relays.Client initiated - The client provides a
nostrconnect://URI. The signer sends aconnectresponse back to the client.
Commands
connect, sign_event, ping, get_public_key, nip04_encrypt, nip04_decrypt, nip44_encrypt, nip44_decrypt, switch_relays.
CLASS METHODS
parse_bunker_uri
my $conn = Net::Nostr::RemoteSigning->parse_bunker_uri($uri_string);
Parses a bunker:// connection URI. Returns a "BunkerConnection" object. Croaks if the URI is malformed or missing the required relay parameter. The secret parameter is optional.
create_bunker_uri
my $uri = Net::Nostr::RemoteSigning->create_bunker_uri(
remote_signer_pubkey => $hex_pubkey, # required
relay => $url_or_arrayref, # required
secret => $secret_string, # optional
);
Creates a bunker:// URI string.
parse_nostrconnect_uri
my $nc = Net::Nostr::RemoteSigning->parse_nostrconnect_uri($uri_string);
Parses a nostrconnect:// connection URI. Returns a "NostrConnect" object. Croaks if the URI is malformed or missing required parameters (relay, secret).
create_nostrconnect_uri
my $uri = Net::Nostr::RemoteSigning->create_nostrconnect_uri(
client_pubkey => $hex_pubkey, # required
relay => $url_or_arrayref, # required
secret => $secret_string, # required
perms => 'nip44_encrypt,sign_event:4', # optional
name => 'My Client', # optional
url => 'https://app.example.com', # optional
image => 'https://app.example.com/i.png', # optional
);
Creates a nostrconnect:// URI string.
request
my $json = Net::Nostr::RemoteSigning->request(
id => 'req-1', # optional, auto-generated if omitted
method => 'ping',
params => [],
);
Builds a JSON-encoded request payload. Croaks if method or params is missing.
request_event
my $event = Net::Nostr::RemoteSigning->request_event(
id => 'req-1',
method => 'sign_event',
params => [$event_json],
pubkey => $client_pubkey,
remote_signer_pubkey => $remote_signer_pubkey,
);
Creates a kind 24133 request Net::Nostr::Event with the JSON payload as unencrypted content and a p tag with the remote signer's pubkey. Croaks if pubkey is missing or not 64-char lowercase hex.
response
my $json = Net::Nostr::RemoteSigning->response(
id => 'req-1',
result => 'pong',
);
Builds a JSON-encoded response payload. Croaks if id is missing.
response_event
my $event = Net::Nostr::RemoteSigning->response_event(
id => 'req-1',
result => 'pong',
pubkey => $remote_signer_pubkey,
client_pubkey => $client_pubkey,
);
Creates a kind 24133 response Net::Nostr::Event with a p tag pointing to the client's pubkey. Croaks if pubkey is missing or not 64-char lowercase hex.
parse_request
my $req = Net::Nostr::RemoteSigning->parse_request($json);
Parses a decrypted JSON request payload. Returns a "Request" object. Croaks if id, method, or params is missing, or if params is not an arrayref.
parse_response
my $resp = Net::Nostr::RemoteSigning->parse_response($json);
Parses a decrypted JSON response payload. Returns a "Response" object. Croaks if id is missing.
parse_permissions
my @perms = Net::Nostr::RemoteSigning->parse_permissions($perms_string);
Parses a comma-separated permission string (e.g. 'nip44_encrypt,sign_event:4'). Returns a list of hashrefs with method and optional param keys.
validate_request
Net::Nostr::RemoteSigning->validate_request($event);
Validates that a request event is kind 24133 and has a p tag. Croaks on failure.
validate_response
Net::Nostr::RemoteSigning->validate_response($event);
Validates that a response event is kind 24133. Croaks on failure.
validate_connect_response
Net::Nostr::RemoteSigning->validate_connect_response($resp, $expected_secret);
Validates that a connect response contains the expected secret. If no expected secret is provided, accepts any response (for bunker-initiated connections where the result is "ack"). Croaks if the secret does not match.
parse_switch_relays
my $relays = Net::Nostr::RemoteSigning->parse_switch_relays($result_string);
Parses a switch_relays response result. Returns an arrayref of relay URLs, or undef if the result is null (indicating no relay change needed).
parse_nip05_metadata
my $meta = Net::Nostr::RemoteSigning->parse_nip05_metadata($json);
Parses a NIP-05 .well-known/nostr.json response containing NIP-46 metadata. Returns a "Nip05Metadata" object. Croaks if the response does not contain a names._ field.
parse_discovery_event
my $disc = Net::Nostr::RemoteSigning->parse_discovery_event($event);
Parses a NIP-89 kind 31990 discovery event. Returns a "Discovery" object. Croaks if the event is not kind 31990 or lacks a k tag with value 24133.
discovery_event
my $event = Net::Nostr::RemoteSigning->discovery_event(
pubkey => $remote_signer_pubkey,
relays => [$relay1, $relay2], # optional
nostrconnect_url => 'https://signer.example.com/<nostrconnect>', # optional
);
Creates a NIP-89 kind 31990 discovery Net::Nostr::Event with a k tag of 24133. Optionally includes relay and nostrconnect_url tags. Croaks if pubkey is missing or not 64-char lowercase hex.
OBJECTS
BunkerConnection
Returned by "parse_bunker_uri". Croaks on unknown arguments.
remote_signer_pubkey- 32-byte hex public key of the remote signerrelays- Arrayref of relay URLssecret- Optional secret for the connection
NostrConnect
Returned by "parse_nostrconnect_uri". Croaks on unknown arguments.
client_pubkey- 32-byte hex public key of the clientrelays- Arrayref of relay URLssecret- Secret string for connection validationperms- Optional comma-separated permissions stringname- Optional client application nameurl- Optional canonical URL of the client applicationimage- Optional image URL for the client application
Nip05Metadata
Returned by "parse_nip05_metadata". Croaks on unknown arguments.
pubkey- 32-byte hex public key from thenames._fieldrelays- Arrayref of relay URLs from thenip46.relaysfieldnostrconnect_url- Optional nostrconnect URL template
Discovery
Returned by "parse_discovery_event". Croaks on unknown arguments.
Request
Returned by "parse_request". Croaks on unknown arguments or missing required fields (id, method, params). params must be an arrayref.
id- Request ID stringmethod- Method name (e.g.'sign_event','ping')params- Arrayref of string parameters
Response
Returned by "parse_response". Croaks on unknown arguments or missing id.
id- Request ID this response corresponds toresult- Result string, or undef on errorerror- Error string, or undef on successis_error- Returns true if this is an error responseis_auth_challenge- Returns true if this is an auth challenge (result is"auth_url")auth_url- Returns the auth URL from the error field if this is an auth challenge
SEE ALSO
NIP-46, Net::Nostr, Net::Nostr::Event, Net::Nostr::Encryption