NAME

Net::Nostr::Negentropy - NIP-77 negentropy set reconciliation

SYNOPSIS

use Net::Nostr::Negentropy;

# Client side
my $client = Net::Nostr::Negentropy->new;
$client->add_item(1000, '01' x 32);
$client->add_item(2000, '02' x 32);
$client->seal;

my $q = $client->initiate;
# Send $q in NEG-OPEN message

# Server side
my $server = Net::Nostr::Negentropy->new;
$server->add_item(1000, '01' x 32);
$server->add_item(3000, '03' x 32);
$server->seal;

my ($response, $s_have, $s_need) = $server->reconcile($q);
# Send $response in NEG-MSG

# Client processes response
my ($next, $c_have, $c_need) = $client->reconcile($response);
# $c_have = ['02' x 32]   -- client has, server doesn't
# $c_need = ['03' x 32]   -- server has, client doesn't
# If $next is defined, send as NEG-MSG and continue

DESCRIPTION

Implements the Negentropy set reconciliation protocol as specified in NIP-77. This protocol efficiently determines the symmetric difference between two sets of events by exchanging fingerprints and progressively narrowing mismatched ranges.

The protocol uses a binary message format with varint encoding, range-based fingerprints (SHA-256 of modular ID sums), and ID lists. Messages are hex-encoded for transmission over the Nostr WebSocket protocol.

Typical flow:

1. Both sides create a Negentropy instance, add their local events via "add_item", and call "seal".
2. The client (initiator) calls "initiate" to produce the initial message.
3. The server calls "reconcile" with the client's message and returns a response.
4. The client calls "reconcile" with the server's response. If the return message is undef, the protocol is complete. Otherwise, send the message and repeat from step 3.

After reconciliation, the client knows which IDs it has that the server lacks (have) and which IDs the server has that it lacks (need). It can then use EVENT to upload and REQ to download.

CONSTRUCTOR

new

my $ne = Net::Nostr::Negentropy->new;
my $ne = Net::Nostr::Negentropy->new(frame_size_limit => 4096);

Creates a new Negentropy instance. Croaks on unknown arguments.

Options:

frame_size_limit -- maximum byte size for encoded protocol messages. When set, "initiate" and "reconcile" croak if an incoming or outgoing binary negentropy frame would exceed the configured limit. Defaults to 0 (unlimited).

METHODS

add_item

$ne->add_item($timestamp, $id_hex);

Adds an event to the local set. $timestamp must be a non-negative integer (the event's created_at). The maximum uint64 value (2**64 - 1) is reserved as the protocol's infinity timestamp and is rejected. $id_hex must be a 64-character lowercase hex string (the event ID). Croaks if called after "seal".

seal

$ne->seal;

Sorts the items and finalizes the set. Must be called before "initiate" or "reconcile". After sealing, "add_item" is no longer allowed.

initiate

my $hex_msg = $ne->initiate;

Creates the initial reconciliation message. Returns a hex-encoded binary message string. This marks the instance as the initiator (client side). Croaks if not sealed.

reconcile

my ($hex_response, $have_ids, $need_ids) = $ne->reconcile($hex_msg);

Processes a reconciliation message from the other side. Returns:

$hex_response -- the response message (hex string), or undef if the protocol is complete
$have_ids -- arrayref of hex ID strings that we have and they don't
$need_ids -- arrayref of hex ID strings that they have and we don't

Croaks if not sealed.

SEE ALSO

NIP-77, Net::Nostr, Net::Nostr::Message