Nostr.Client 2.0.0

dotnet add package Nostr.Client --version 2.0.0                
NuGet\Install-Package Nostr.Client -Version 2.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nostr.Client" Version="2.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Nostr.Client --version 2.0.0                
#r "nuget: Nostr.Client, 2.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Nostr.Client as a Cake Addin
#addin nuget:?package=Nostr.Client&version=2.0.0

// Install Nostr.Client as a Cake Tool
#tool nuget:?package=Nostr.Client&version=2.0.0                

Logo

Nostr client

.NET Core NuGet version NuGet downloads

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.

Releases and breaking changes

License:

Apache License 2.0

Features

  • installation via NuGet (Nostr.Client)
  • targeting .NET 6.0 and higher (.NET Core, Linux/MacOS compatible)
  • reactive extensions (Rx.NET)

Usage

Receiving events
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();
Sending event
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));
Sending encrypted direct message (NIP-04)
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));
Multi relays support
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:

image

NIP's coverage

  • 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 and p 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!

Reconnecting

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.

Testing

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();

Multi-threading and other considerations

See Websocket Client readme

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Nostr.Client:

Package Downloads
NostrServices.Client

Client wrapper for https://nostr.api.v0l.io

PlangLibrary

Plang language core

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on Nostr.Client:

Repository Stars
v0l/void.cat
Free file hosting website
Version Downloads Last updated
2.0.0 2,618 11/16/2023
1.4.3 2,014 8/1/2023
1.4.2 601 6/25/2023
1.4.1 472 6/21/2023
1.4.0 535 4/28/2023
1.3.1 580 4/20/2023
1.3.0 525 4/19/2023
1.2.4 531 4/17/2023
1.2.3 559 4/4/2023
1.2.2 558 3/10/2023
1.2.1 544 3/7/2023
1.2.0 559 3/3/2023
1.1.2 562 2/23/2023
1.1.1 586 2/23/2023
1.1.0 559 2/23/2023
1.0.4 562 2/21/2023
1.0.3 602 2/18/2023
1.0.2 549 2/18/2023
1.0.1 591 2/18/2023
1.0.0 591 2/18/2023

Enhancements