Sync & Negentropy
The @nostr-dev-kit/sync
package provides efficient event synchronization using the NIP-77 Negentropy protocol. Instead of fetching all events, negentropy uses set reconciliation to identify and transfer only the differences between your local cache and relay data.
Key Benefits
- 10-100x Bandwidth Reduction: Only sync what you don't have
- Cache Integration: Automatically works with NDK cache adapters
- Simple API:
await ndk.sync(filters)
- Multi-Relay Support: Sequential sync across multiple relays with deduplication
- Live Updates:
syncAndSubscribe
pattern for historical + real-time events
Installation
bash
npm install @nostr-dev-kit/sync
# or
bun add @nostr-dev-kit/sync
Quick Start
typescript
import NDK from '@nostr-dev-kit/ndk';
import { ndkSync } from '@nostr-dev-kit/sync';
const ndk = new NDK({
explicitRelayUrls: ['wss://relay.damus.io'],
cacheAdapter: myCacheAdapter // Required!
});
await ndk.connect();
// Sync recent notes
const result = await ndkSync.call(ndk, {
kinds: [1],
authors: [pubkey],
since: Math.floor(Date.now() / 1000) - 86400
});
console.log(`Synced ${result.events.length} events`);
Core Features
Basic Sync
The simplest way to sync events:
typescript
const result = await ndkSync.call(ndk, filters);
Returns:
events
: Array of fetched events (ifautoFetch: true
)need
: Set of event IDs we needed from relayshave
: Set of event IDs we already had
Sync + Subscribe Pattern
For seamless historical + live event coverage:
typescript
import { syncAndSubscribe } from '@nostr-dev-kit/sync';
const sub = await syncAndSubscribe.call(ndk,
{ kinds: [1], authors: [pubkey] },
{
onEvent: (event) => console.log('Event:', event.content),
onRelaySynced: (relay, count) => console.log(`Synced ${count} from ${relay.url}`),
onSyncComplete: () => console.log('All relays synced!')
}
);
How it works:
- Immediately starts subscription (non-blocking)
- Background: syncs historical events from each relay
- All events flow to the same subscription
- Perfect for wallets, feeds, DMs
Relay Capability Detection
Check if relays support NIP-77 before syncing:
typescript
import { supportsNegentropy, filterNegentropyRelays } from '@nostr-dev-kit/sync';
// Check single relay
const supported = await supportsNegentropy('wss://relay.damus.io');
// Filter relay list
const allRelays = ['wss://relay1.com', 'wss://relay2.com'];
const syncRelays = await filterNegentropyRelays(allRelays);
Options
NDKSyncOptions
typescript
interface NDKSyncOptions {
// Relay selection
relaySet?: NDKRelaySet; // Explicit relay set
relayUrls?: string[]; // Or relay URLs
// Behavior
autoFetch?: boolean; // Auto-fetch events (default: true)
frameSizeLimit?: number; // Message size limit (default: 50000)
}
Advanced Usage
Manual Negentropy
For custom use cases:
typescript
import { Negentropy, NegentropyStorage } from '@nostr-dev-kit/sync';
const storage = NegentropyStorage.fromEvents(events);
const neg = new Negentropy(storage, 50000);
const initialMsg = await neg.initiate();
const { nextMessage, have, need } = await neg.reconcile(response);
Performance
Negentropy is extremely bandwidth-efficient:
- Small differences: ~1-2 KB to sync 1000s of events
- Large differences: Scales logarithmically
- No differences: Single round-trip (~100 bytes)
Relay Compatibility
Most relays don't support NIP-77 yet. Known compatible relays:
- wss://relay.damus.io (strfry)
- wss://nos.lol (strfry)
- wss://relay.snort.social (strfry)
Always check relay support before syncing!
Examples
See the package README for more examples and the check-relay-support example for relay capability checking.