nostr_signaling 0.4.0
nostr_signaling: ^0.4.0 copied to clipboard
Nostr-based signaling library for Dart enabling arbitrary data exchange between peers with optional GZip compression and multi-relay redundancy.
nostr_signaling #
A Nostr-based signaling library for Dart. Exchange arbitrary binary data between Nostr peers, with optional GZip compression and multi-relay redundancy.
Features #
- Nostr signaling — publish and subscribe to arbitrary binary data over the Nostr protocol.
- GZip compression — reduce payload size automatically; detects and rejects already-compressed data.
- Multi-relay — publish/subscribe across multiple Nostr relays for reliability and redundancy.
- Key management — generate, import, and validate Nostr key pairs.
- Pluggable engine — implement
ICompressionEngineto add custom compression algorithms.
Getting started #
Add nostr_signaling to your pubspec.yaml:
dependencies:
nostr_signaling: ^0.4.0
Usage #
Basic signaling (no compression) #
import 'package:nostr_signaling/nostr_signaling.dart';
void main() async {
final signaling = NostrSignalingFactory.create(
keyPair: keyPair,
);
await signaling.connect();
// Publish raw bytes to a peer
final eventId = await signaling.publish([1, 2, 3, 4, 5]);
// Subscribe to messages from a peer
await signaling.subscribe('peer-pubkey-hex', (id, data) {
print('Received from $id: $data');
});
await signaling.disconnect();
}
With GZip compression #
final signaling = NostrSignalingFactory.create(
keyPair: keyPair,
useCompression: true,
);
await signaling.connect();
// Large data is compressed automatically before publishing
final largeData = List<int>.generate(10000, (i) => i % 256);
await signaling.publish(largeData);
Key generation #
// Generate a new random key pair
final keyPair = NostrKeys.generate();
print('Private: ${keyPair.privateKey}');
print('Public: ${keyPair.publicKey}');
// Import from an existing private key
final imported = NostrKeys.fromPrivateKeyHex('your-private-key-hex');
// Import and validate a full key pair
final validated = NostrKeys.fromHex(
privateKeyHex: 'private-key-hex',
publicKeyHex: 'public-key-hex',
);
Custom compression engine #
class MyCompressionEngine implements ICompressionEngine {
@override
Future<CompressedData> compress(List<int> data) async {
// Your custom compression logic
return CompressedData(
originalSize: data.length,
compressedSize: data.length,
compressionRatio: 0,
data: data,
timestamp: DateTime.now().millisecondsSinceEpoch,
);
}
@override
Future<List<int>> decompress(CompressedData compressed) async {
// Your custom decompression logic
return compressed.data;
}
}
final signaling = NostrSignalingFactory.create(
keyPair: keyPair,
useCompression: true,
compressionEngine: MyCompressionEngine(),
);
Event deduplication #
EventCallback is now a class with built-in dedup. The same event hash
is never processed twice.
final callback = EventCallback(
(id, data) => print('Received from $id: ${data.length} bytes'),
maxRecords: 5000, // keep last 5000 hashes
);
Multi-relay redundancy #
final signaling = NostrSignalingFactory.create(
keyPair: keyPair,
relayUrls: [
'wss://relay.damus.io',
'wss://nos.lol',
'wss://relay.primal.net',
],
);
Config file #
NostrConfig persists signaling configuration to a JSON file on disk.
| Field | Type | Default | Description |
|---|---|---|---|
relays |
List<String> |
10 standard Nostr relays | Relay WebSocket URLs to publish/subscribe |
keyPair |
NostrKeyPair? |
null |
Nostr key pair (private + public key hex) |
collection |
String |
'nostr_signaling_seen_hashes' |
Collection name for event callback dedup |
Example nostr_config.json:
{
"relays": ["wss://relay.damus.io", "wss://nos.lol"],
"collection": "nostr_signaling_seen_hashes",
"privateKey": "hex-private-key",
"publicKey": "hex-public-key"
}
// Create and save
final config = NostrConfig(
keyPair: myKeyPair,
relays: ['wss://relay.damus.io'],
);
await config.save(); // → nostr_config.json
// Load (auto-creates with new key pair if file missing)
final loaded = await NostrConfig.load(); // async
final loadedSync = NostrConfig.loadSync(); // sync
Initial point from config (no required parameters) #
Loads keyPair and relays from the config file automatically.
Creates the file with a fresh key pair if missing. Throws StateError if the
file exists but has no key pair.
// Singleton DI — no parameters needed
await initialPointNostrSignalingFromConfig();
final signaling = getINostrSignaling();
// Registry DI — supports multiple named instances
initialPointNostrSignalingRegistryFromConfig(key: 'alice');
initialPointNostrSignalingRegistryFromConfig(key: 'bob');
Both accept an optional configPath (default: nostr_config.json).
API overview #
| Class / Interface | Description |
|---|---|
INostrSignaling |
Abstract signaling interface (connect, publish, subscribe, etc.) |
NostrSignalingImpl |
Concrete signaling implementation |
INostrRelay |
Abstract relay interface |
NostrRelayImpl |
Concrete WebSocket relay via dart_nostr |
ICompressionEngine |
Pluggable compression interface |
GzipCompressionEngine |
GZip compression with auto-detection |
EventCallback |
Callback wrapper with automatic hash-based deduplication |
PayloadHashLength |
Payload hash length (32/64/256 bits) |
NostrConfig |
Config file persistence (relays + key pair) |
NostrSignalingFactory |
Factory with create() and createWithCustomRelays() |
NostrKeys / NostrKeyPair |
Key generation, import, and validation |
CompressedData |
Compression metadata (original size, ratio, timestamp) |
NostrTestKeys / NostrTestRelays |
Test constants |
Event kinds #
| Kind | Description |
|---|---|
1000 |
Compressed payload (base64-encoded) |
1001 |
Raw payload (base64-encoded) |
Additional information #
- Repository
- Issue tracker
- License: MIT