This is a C# implementation of the Nostr protocol found here:
https://github.com/nostr-protocol/nips
Nostr protocol is based on websocket communication. This library keeps a reliable connection to get real-time data and fast execution of your commands.
Apache License 2.0
- installation via NuGet (Nostr.Client)
- targeting .NET 6.0 and higher (.NET Core, Linux/MacOS compatible)
- reactive extensions (Rx.NET)
var url = new Uri("wss://relay.damus.io");
using var communicator = new NostrWebsocketCommunicator(url);
using var client = new NostrWebsocketClient(communicator, null);
client.Streams.EventStream.Subscribe(response =>
{
var ev = response.Event;
Log.Information("{kind}: {content}", ev?.Kind, ev?.Content)
if(ev is NostrMetadataEvent evm) {
Log.Information("Name: {name}, about: {about}", evm.Metadata?.Name, evm.Metadata?.About);
}
});
await communicator.Start();
var ev = new NostrEvent
{
Kind = NostrKind.ShortTextNote,
CreatedAt = DateTime.UtcNow,
Content = "Test message from C# client"
};
var key = NostrPrivateKey.FromBech32("nsec1xxx");
var signed = ev.Sign(key);
client.Send(new NostrEventRequest(signed));
var sender = NostrPrivateKey.FromBech32("nsec1l0a7m5dlg4h9wurhnmgsq5nv9cqyvdwsutk4yf3w4fzzaqw7n80ssdfzkg");
var receiver = NostrPublicKey.FromBech32("npub1dd668dyr9un9nzf9fjjkpdcqmge584c86gceu7j97nsp4lj2pscs0xk075");
var ev = new NostrEvent
{
CreatedAt = DateTime.UtcNow,
Content = $"Test private message from C# client"
};
var encrypted = ev.EncryptDirect(sender, receiver);
var signed = encrypted.Sign(sender);
client.Send(new NostrEventRequest(signed));
var relays = new[]
{
new NostrWebsocketCommunicator(new Uri("wss://relay.snort.social")),
new NostrWebsocketCommunicator(new Uri("wss://relay.damus.io")),
new NostrWebsocketCommunicator(new Uri("wss://nos.lol"))
};
var client = new NostrMultiWebsocketClient(NullLogger<NostrWebsocketClient>.Instance, relays);
client.Streams.EventStream.Subscribe(HandleEvent);
relays.ToList().ForEach(relay => relay.Start());
More usage examples:
- NIP-01: Basic protocol flow description
- NIP-02: Contact List and Petnames (No petname support)
- NIP-03: OpenTimestamps Attestations for Events
- NIP-04: Encrypted Direct Message
- NIP-05: Mapping Nostr keys to DNS-based internet identifiers
- NIP-06: Basic key derivation from mnemonic seed phrase
- NIP-07:
window.nostr
capability for web browsers - NIP-08: Handling Mentions
- NIP-09: Event Deletion
- NIP-10: Conventions for clients' use of
e
andp
tags in text events - NIP-11: Relay Information Document
- NIP-12: Generic Tag Queries
- NIP-13: Proof of Work
- NIP-14: Subject tag in text events
- NIP-15: End of Stored Events Notice
- NIP-19: bech32-encoded entities
- NIP-20: Command Results
- NIP-21:
nostr:
Protocol handler (web+nostr
) - NIP-25: Reactions
- NIP-26: Delegated Event Signing (Display delegated signings only)
- NIP-28: Public Chat
- NIP-36: Sensitive Content
- NIP-40: Expiration Timestamp
- NIP-42: Authentication of clients to relays
- NIP-50: Search
- NIP-51: Lists
- NIP-65: Relay List Metadata
Pull Requests are welcome!
A built-in reconnection invokes after 1 minute (default) of not receiving any messages from the server.
It is possible to configure that timeout via communicator.ReconnectTimeout
.
Also, a stream ReconnectionHappened
sends information about a type of reconnection.
However, if you are subscribed to low-rate channels, you will likely encounter that timeout - higher it to a few minutes or implement ping-pong
interaction on your own every few seconds.
In the case of Nostr relay outage, there is a built-in functionality that slows down reconnection requests
(could be configured via client.ErrorReconnectTimeout
, the default is 1 minute).
Beware that you need to resubscribe to channels after reconnection happens. You should subscribe to ReconnectionHappened
stream and send subscription requests.
The library is prepared for replay testing. The dependency between Client
and Communicator
is via abstraction INostrCommunicator
. There are two communicator implementations:
NostrWebsocketCommunicator
- real-time communication with Nostr relay.NostrFileCommunicator
- a simulated communication, raw data are loaded from files and streamed.
Feel free to implement INostrCommunicator
on your own, for example, load raw data from database, cache, etc.
Usage:
var communicator = new NostrFileCommunicator();
communicator.FileNames = new[]
{
"data/nostr-data.txt"
};
communicator.Delimiter = "\n";
var client = new NostrWebsocketClient(communicator);
client.Streams.EventStream.Subscribe(trade =>
{
// do something with an event
});
await communicator.Start();