@nostrwatch/negentropy
v0.0.1
Published
Utilities for [NIP-77](https://github.com/nostr-protocol/nips/blob/master/77.md) Negentropy set reconciliation between Nostr clients and relays.
Readme
@nostrwatch/negentropy-utils
Utilities for NIP-77 Negentropy set reconciliation between Nostr clients and relays.
Overview
@nostrwatch/negentropy-utils implements the NIP-77 Negentropy protocol — a set reconciliation algorithm that efficiently identifies which Nostr events (signed JSON objects that are the fundamental unit of the Nostr protocol) a client and relay do not have in common. Instead of transferring full event lists, Negentropy uses fingerprint-based range comparisons to minimize data transferred during sync. The package provides ClientHandler for use in Nostr clients and ServerHandler for use in relay implementations, both operating over a pluggable Transport interface.
Prerequisites
Node.js >=20 and pnpm >=9.
Installation
pnpm add @nostrwatch/negentropy-utilsOr with npm:
npm install @nostrwatch/negentropy-utilsQuick Start
import {ClientHandler} from '@nostrwatch/negentropy-utils'
import {WebSocketTransport} from '@nostrwatch/negentropy-utils/transports/WebSocket'
import type {RecordItem} from '@nostrwatch/negentropy-utils'
// Local event records (timestamp + 32-byte ID)
const localRecords: RecordItem[] = [
{timestamp: BigInt(1700000000), id: new Uint8Array(32).fill(1)},
{timestamp: BigInt(1700000001), id: new Uint8Array(32).fill(2)}
]
const ws = new WebSocket('wss://relay.damus.io')
const transport = new WebSocketTransport(ws)
// Subscribe to kind 1 events and start sync
const client = new ClientHandler(localRecords, transport, {kinds: [1]}, 'sync-1')
ws.onopen = () => {
client.startSync()
// Sends NEG-OPEN to the relay and begins reconciliation
}API
ClientHandler
Used on the client side to synchronize a local event set with a relay.
class ClientHandler {
constructor(
records: RecordItem[],
transport: Transport,
filter: object,
subscriptionId: string
)
startSync(): void
handleMessage(message: string): void
}constructor(records, transport, filter, subscriptionId)
| Parameter | Type | Description |
|-----------|------|-------------|
| records | RecordItem[] | Local event records to synchronize |
| transport | Transport | Message transport (e.g., WebSocketTransport) |
| filter | object | Nostr filter (e.g., {kinds: [1]}) scoping which events to sync |
| subscriptionId | string | Unique identifier for this sync session |
startSync()
Initiates the NIP-77 handshake by sending a NEG-OPEN message to the relay. Call this after the transport connection is open.
handleMessage(message: string)
Processes an incoming relay message (NEG-MSG, NEG-ERR, or NEG-CLOSE). The Transport implementation calls this automatically via onMessage.
ServerHandler
Used on the relay side to respond to client sync requests.
class ServerHandler {
constructor(
records: RecordItem[],
transport: Transport,
applyFilter: (records: RecordItem[], filter: object) => Promise<RecordItem[]>
)
handleMessage(message: string): Promise<void>
}constructor(records, transport, applyFilter)
| Parameter | Type | Description |
|-----------|------|-------------|
| records | RecordItem[] | All event records stored on the relay |
| transport | Transport | Message transport connected to the client |
| applyFilter | function | Async function that filters records by a Nostr filter object |
handleMessage(message: string)
Handles incoming client messages (NEG-OPEN, NEG-MSG, NEG-CLOSE). The Transport implementation calls this automatically.
Transport
An abstract interface that both handlers communicate through. Implement it for any transport layer (WebSocket, stdio, etc.).
abstract class Transport {
abstract send(message: string): void
abstract onMessage(handler: (message: string) => void): void
}Types
type RecordItem = {timestamp: bigint; id: Uint8Array}
type RangeMode = 0 | 1 | 2
interface Range {
upperBound: Bound
mode: RangeMode
payload: Uint8Array
}
interface Bound {
timestampOffset: bigint
idPrefix: Uint8Array
}Known Limitations
No known limitations at this time.
Agent Skills
No agent skills defined yet for this package.
Related Packages
@nostrwatch/worker-relay— in-browser relay; negentropy-utils can be used with it for client-side event sync@nostrwatch/route66— relay monitoring library that uses negentropy for event set synchronization
