NAME
Net::Nostr::Identifier - Mapping Nostr keys to DNS-based internet identifiers
SYNOPSIS
use AnyEvent;
use Net::Nostr::Identifier;
my $pubkey = 'b0635d6a9851d3aed0cd6c495b282167acf761729078d975fc341b22650b07b9';
# Parse and validate an identifier
my ($local, $domain) = Net::Nostr::Identifier->parse('bob@example.com');
# Build the well-known URL
my $url = Net::Nostr::Identifier->url('bob@example.com');
# https://example.com/.well-known/nostr.json?name=bob
# Display name (root identifier _@domain shows as domain)
Net::Nostr::Identifier->display_name('bob@example.com'); # "bob@example.com"
Net::Nostr::Identifier->display_name('_@bob.com'); # "bob.com"
# Verify a nostr.json response
my $response = { names => { bob => $pubkey } };
if (Net::Nostr::Identifier->verify_response($response, 'bob', $pubkey)) {
print "Verified!\n";
}
# Async verification via HTTP (requires AnyEvent and AnyEvent::HTTP)
my $cv = AnyEvent->condvar;
my $ident = Net::Nostr::Identifier->new;
$ident->verify(
identifier => 'bob@example.com',
pubkey => $pubkey,
on_success => sub { my ($relays) = @_; print "Verified!\n"; $cv->send },
on_failure => sub { my ($reason) = @_; warn "Failed: $reason"; $cv->send },
);
$cv->recv;
# Async lookup (find pubkey from identifier)
$cv = AnyEvent->condvar;
$ident->lookup(
identifier => 'bob@example.com',
on_success => sub { my ($pubkey, $relays) = @_; $cv->send },
on_failure => sub { my ($reason) = @_; $cv->send },
);
$cv->recv;
DESCRIPTION
Implements NIP-05 for mapping Nostr public keys to DNS-based internet identifiers. An identifier has the form local-part@domain where the local-part uses only characters a-z0-9-_..
Clients verify an identifier by fetching https://<domain>/.well-known/nostr.json?name=<local-part> and checking that the returned "names" mapping contains the expected public key.
The special identifier _@domain is treated as a root identifier and may be displayed as just the domain.
OPTIONAL HTTP DEPENDENCY
Pure helpers such as parse, url, display_name, verify_response, and extract_relays do not load an HTTP client.
The network methods lookup and verify lazy-load AnyEvent::HTTP. The Net::Nostr::Core distribution recommends AnyEvent::HTTP but does not require it. Installing the Net::Nostr shim distribution pulls it in as a hard dependency for the full-stack install.
CLASS METHODS
parse
my ($local_part, $domain) = Net::Nostr::Identifier->parse('bob@example.com');
Splits an identifier into its local-part and domain. Strictly validates both parts. Croaks if the identifier is invalid:
missing or multiple
@empty local-part or domain
local-part contains characters outside
a-z0-9-_.domain contains whitespace,
/,:,?,#,[,], or control characters
Bracketed IPv6 addresses (e.g. [::1]) are rejected because NIP-05 is DNS-based. Ports are rejected because the spec constructs HTTPS URLs from the domain directly.
url
my $url = Net::Nostr::Identifier->url('bob@example.com');
Returns the well-known URL for verifying the identifier.
display_name
my $name = Net::Nostr::Identifier->display_name('bob@example.com'); # "bob@example.com"
my $root = Net::Nostr::Identifier->display_name('_@bob.com'); # "bob.com"
Returns the display form of the identifier. Root identifiers (_@domain) are displayed as just the domain. All other identifiers are returned as-is.
verify_response
my $ok = Net::Nostr::Identifier->verify_response($hashref, $local_part, $pubkey);
Returns true if the response hashref maps the given local-part to the given pubkey. Keys must be 64-character lowercase hex (32-byte public keys).
extract_relays
my $relays = Net::Nostr::Identifier->extract_relays($hashref, $pubkey);
Returns an arrayref of relay URLs for the given pubkey, or an empty arrayref if none are found.
CONSTRUCTOR
new
Accepts named arguments as either a flat list or a single hash reference.
my $ident = Net::Nostr::Identifier->new(%args);
Croaks on unknown arguments.
- base_url
-
Optional base URL override for testing. When set, HTTP requests go to this URL instead of
https://<domain>.
INSTANCE METHODS
verify
$ident->verify(
identifier => 'bob@example.com',
pubkey => $hex_pubkey,
on_success => sub { my ($relays) = @_ },
on_failure => sub { my ($reason) = @_ },
);
Asynchronously fetches the well-known URL and verifies that the identifier maps to the given pubkey. HTTP redirects are ignored per the spec. Requires AnyEvent and AnyEvent::HTTP. Croaks if any required argument is missing, if on_success/on_failure are not CODE refs, or if AnyEvent::HTTP is not installed.
The on_success callback receives an arrayref of relay URLs (may be empty). The on_failure callback receives an error reason string.
lookup
$ident->lookup(
identifier => 'bob@example.com',
on_success => sub { my ($pubkey, $relays) = @_ },
on_failure => sub { my ($reason) = @_ },
);
Asynchronously fetches the well-known URL and returns the pubkey associated with the identifier. HTTP redirects are ignored per the spec. Requires AnyEvent and AnyEvent::HTTP. Croaks if any required argument is missing, if on_success/on_failure are not CODE refs, or if AnyEvent::HTTP is not installed.
The on_success callback receives the hex pubkey and an arrayref of relay URLs. The on_failure callback receives an error reason string.
SECURITY
The /.well-known/nostr.json endpoint MUST NOT return any HTTP redirects. This module ignores all HTTP redirects, treating them as verification failures.