rust-nostr

Project Homepage | Repository | Become a supporter

The nostr development kit to build fast, stable and efficient apps for any environment!

Why rust-nostr?

  • Multi-Language Support: We support native API's including Rust, Python, JavaScript, Kotlin and Swift so you can build nostr apps in your preferred programming language.

  • Multi-Platform Support: Write nostr apps for desktop, server, mobile, web and/or embedded devices!

  • WASM Compatibility: Most of our libraries compile to WebAssembly so that they can be integrated into web applications.

  • High performance: Powered by Rust's unparalleled performance and memory safety, our libraries offers speed, stability and reliability. The same features are extended to its bindings in other languages, ensuring optimal stability across diverse development environments.

  • Broad NIP Support: Support to most relevant NIPs.

  • Customizable: The libraries are built in modular way, allowing to build customized nostr apps.

Libraries

  • Nostr: Implementation of the nostr protocol
  • Nostr Database: Databases abstraction, indexes and in-memory database implementation
  • Nostr SDK: High level nostr client library

State

These libraries are in ALPHA state, things that are implemented generally work but the API will change in breaking ways.

Donations

rust-nostr is free and open-source. This means we do not earn any revenue by selling it. Instead, we rely on your financial support. If you actively use any of the rust-nostr libs/software/services, then please donate.

License

This project is distributed under the MIT software license.

Nostr

This section include documentation for the nostr library for all the supported languages (Rust and bindings).

If you're writing a typical Nostr client or bot, you may be interested in nostr-sdk.

Installing the library

Rust

Add the nostr dependency in your Cargo.toml file:

[dependencies]
nostr = "0.30"

Alternatively, you can add it directly from git source:

[dependencies]
nostr = { git = "https://github.com/rust-nostr/nostr", tag = "v0.30.0" }

Info

To use a specific commit, use rev instead of tag.

Import the library in your code:

use nostr::prelude::*;
Python

The nostr-protocol package is available on the public PyPI:

pip install nostr-protocol 

Alternatively, you can manually add the dependency in your requrements.txt, setup.py, etc.:

nostr-protocol==0.11.0

Import the library in your code:

from nostr_protocol import *

Support matrix

The wheels are distributed for the following python versions and platforms. If your version/platform is not currently supported, you can compile the wheel by your self following these instructions.

Python version

3.83.93.103.113.123.13

Platform support

OSi686x64aarch64arm
Linux
macOS
Windows
JavaScript

The nostr package is available on the public npmjs:

npm i @rust-nostr/nostr 

Alternatively, you can manually add the dependency in your package.json file:

{
    "dependencies": {
        "@rust-nostr/nostr": "0.13.0"
    }
}

WASM

This library to work require to load the WASM code.

Load in async context

const { loadWasmAsync } = require("@rust-nostr/nostr");

async function main() {
    // Load WASM
    await loadWasmAsync();

    // ...
}

main();

Load in sync context

const { loadWasmSync } = require("@rust-nostr/nostr");

function main() {
    // Load WASM
    loadWasmSync();

    // ...
}

main();
Kotlin

To use the Kotlin language bindings for nostr in your Android project add the following to your gradle dependencies:

repositories {
    mavenCentral()
}

dependencies { 
    implementation("io.github.rust-nostr:nostr:0.11.0")
}

Import the library in your code:

import rust.nostr.protocol.*

Known issues

JNA dependency

Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be

class file for com.sun.jna.Pointer not found

The solution is to add JNA as a dependency like so:

dependencies {
    // ...
    implementation("net.java.dev.jna:jna:5.12.0@aar")
}
Swift

Xcode

Via File > Add Packages..., add

https://github.com/rust-nostr/nostr-swift.git

as a package dependency in Xcode.

Swift Package

Add the following to the dependencies array in your Package.swift:

.package(url: "https://github.com/rust-nostr/nostr-swift.git", from: "0.11.0"),

Import the library in your code:

import Nostr

Keys

Generate new random keys

To generate a new key pair use the generate() method:

Rust
pub fn generate() -> Result<()> {
    let keys = Keys::generate();

    let public_key = keys.public_key();
    let secret_key = keys.secret_key()?;

    println!("Public key (hex): {}", public_key);
    println!("Public key (bech32): {}", public_key.to_bech32()?);
    println!("Secret key (hex): {}", keys.secret_key()?.to_secret_hex());
    println!("Secret key (bech32): {}", secret_key.to_bech32()?);
    
    Ok(())
}
Python
def generate():
    keys = Keys.generate()
    public_key = keys.public_key()
    secret_key = keys.secret_key()

    print("Keys:")
    print(" Public keys:")
    print(f"     hex:    {public_key.to_hex()}")
    print(f"     bech32: {public_key.to_bech32()}")
    print(" Secret keys:")
    print(f"     hex:    {secret_key.to_hex()}")
    print(f"     bech32: {secret_key.to_bech32()}")


JavaScript
function generate() {
    // Load WASM
    loadWasmSync();

    // Generate new random keys
    let keys = Keys.generate();
    console.log("Public key (hex): ", keys.publicKey.toHex());
    console.log("Secret key (hex): ", keys.secretKey.toHex());

    console.log("Public key (bech32): ", keys.publicKey.toBech32());
    console.log("Secret key (bech32): ", keys.secretKey.toBech32());
}
Kotlin
fun generate() {
    val keys = Keys.generate();

    val publicKey = keys.publicKey();
    val secretKey = keys.secretKey();

    println("Public key (hex): ${publicKey.toHex()}");
    println("Public key (bech32): ${publicKey.toBech32()}");

    println("Secret key (hex): ${secretKey.toHex()}");
    println("Secret key (bech32): ${secretKey.toHex()}");
}
Swift
import Nostr
import Foundation

func keys() {
    // TODO
}

Restore from hex and/or bech32 secret key

Rust
pub fn restore() -> Result<()> {
    // Parse keys directly from secret key
    let keys = Keys::parse("secret-key")?;
    
    // Parse secret key and construct keys
    let secret_key = SecretKey::parse("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")?;
    let keys = Keys::new(secret_key);

    // Restore from bech32
    let secret_key = SecretKey::from_bech32("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")?;
    let keys = Keys::new(secret_key);

    // Restore from hex
    let secret_key = SecretKey::from_hex("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")?;
    let keys = Keys::new(secret_key);

    Ok(())
}
Python
def restore():
    keys = Keys.parse("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")

    secret_key = SecretKey.from_hex("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")
    keys = Keys(secret_key)

    secret_key = SecretKey.from_bech32("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")
    keys = Keys(secret_key)


JavaScript
function restore() {
    // Load WASM
    loadWasmSync();

    // Parse Keys directly from secret key
    let keys1 = Keys.parse("nsec1ufnus6pju578ste3v90xd5m2decpuzpql2295m3sknqcjzyys9ls0qlc85");

    // Parse secret key and construct keys
    let secretKey = SecretKey.fromBech32("nsec1ufnus6pju578ste3v90xd5m2decpuzpql2295m3sknqcjzyys9ls0qlc85");
    let keys2 = new Keys(secretKey);
    console.log("Secret key (hex): ", keys2.secretKey.toHex());

    // Construct Keys from public key
    let publicKey = PublicKey.fromHex("7b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e");
    let keys3 = Keys.fromPublicKey(publicKey);
}
Kotlin
fun restore() {
    var keys = Keys.parse("hex or bech32 secret key")

    // Parse from hex
    var secretKey = SecretKey.fromHex("6b911fd37cdf5c81d4c0adb1ab7fa822ed253ab0ad9aa18d77257c88b29b718e")
    keys = Keys(secretKey = secretKey)

    secretKey = SecretKey.fromBech32("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")
    keys = Keys(secretKey = secretKey)
}
Swift

TODO

Generate vanity keys

Rust
pub fn vanity() -> Result<()> {
    let keys = Keys::vanity(vec!["0000", "yuk", "yuk0"], true, 8)?;
    println!("Secret key: {}", keys.secret_key()?.to_bech32()?);
    println!("Public key: {}", keys.public_key().to_bech32()?);
    Ok(())
}
Python
def vanity():
    keys = Keys.vanity(["yuk0"], True, 8)

    print(f"Public keys: {keys.public_key().to_bech32()}")
    print(f"Secret keys: {keys.secret_key().to_bech32()}")
JavaScript
function vanity() {
    // Load WASM
    loadWasmSync();

    // NOTE: NOT SUPPORTED YET!

    // Generate vanity keys
    // let keys = Keys.vanity(["yuk0"], true, 1);
    // console.log("Public key (bech32): ", keys.publicKey.toBech32());
    // console.log("Secret key (bech32): ", keys.secretKey.toBech32());
}
Kotlin
fun vanity() {
    val keys = Keys.vanity(listOf("yuk0"), true, 4u)

    println("Public key: ${keys.publicKey().toBech32()}");
    println("Secret key: ${keys.secretKey().toHex()}");
}
Swift
import Nostr
import Foundation

func vanity() {
    // TODO
}

Event

Serialize/deserialize to/from JSON

Rust
use nostr::prelude::*;

pub fn event() -> Result<()> {
    // Deserialize from json
    let json = r#"{"content":"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==","created_at":1640839235,"id":"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45","kind":4,"pubkey":"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785","sig":"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd","tags":[["p","13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d"]]}"#;
    let event = Event::from_json(json)?;

    // Serialize as json
    let json = event.as_json();
    println!("{json}");

    Ok(())
}
Python
from nostr_protocol import *

def event_json():
    # Deserialize from json
    json = '{"content":"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==","created_at":1640839235,"id":"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45","kind":4,"pubkey":"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785","sig":"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd","tags":[["p","13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d"]]}'
    event = Event.from_json(json)

    # Serialize as json
    json = event.as_json()
    print(f"{json}")
JavaScript
const { loadWasmSync, Event } = require("@rust-nostr/nostr");

function eventJson() {
    // Load WASM
    loadWasmSync();

    // Deserialize from json
    let json1 = '{"content":"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==","created_at":1640839235,"id":"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45","kind":4,"pubkey":"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785","sig":"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd","tags":[["p","13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d"]]}'
    let event = Event.fromJson(json1)

    // Serialize as json
    let json2 = event.asJson()
    console.log(json2);
}

module.exports.eventJson = eventJson;
Kotlin
fun json() {
    // Deserialize from json
    val json =
        "{\"content\":\"uRuvYr585B80L6rSJiHocw==?iv=oh6LVqdsYYol3JfFnXTbPA==\",\"created_at\":1640839235,\"id\":\"2be17aa3031bdcb006f0fce80c146dea9c1c0268b0af2398bb673365c6444d45\",\"kind\":4,\"pubkey\":\"f86c44a2de95d9149b51c6a29afeabba264c18e2fa7c49de93424a0c56947785\",\"sig\":\"a5d9290ef9659083c490b303eb7ee41356d8778ff19f2f91776c8dc4443388a64ffcf336e61af4c25c05ac3ae952d1ced889ed655b67790891222aaa15b99fdd\",\"tags\":[[\"p\",\"13adc511de7e1cfcf1c6b7f6365fb5a03442d7bcacf565ea57fa7770912c023d\"]]}"
    val event = Event.fromJson(json)

    // Serialize as json
    println(event.asJson())
}
Swift
import Nostr
import Foundation

func json() {
    // TODO
}

Compose with event builder

A convenient way to compose events is by using the EventBuilder. It allow to compose standard and/or custom events.

Rust
use nostr::prelude::*;

pub fn event() -> Result<()> {
    let keys = Keys::generate();

    // Compose custom event
    let custom_event = EventBuilder::new(Kind::Custom(1111), "", []).to_event(&keys)?;

    // Compose text note
    let textnote_event = EventBuilder::text_note("Hello", []).to_event(&keys)?;

    // Compose reply to above text note
    let reply_event = EventBuilder::text_note("Reply to hello", [Tag::event(textnote_event.id)])
        .to_event(&keys)?;

    // Compose POW event
    let pow_event =
        EventBuilder::text_note("Another reply with POW", [Tag::event(textnote_event.id)])
            .to_pow_event(&keys, 20)?;

    Ok(())
}
Python
from nostr_protocol import *

def event_builder():
    keys = Keys.generate()

    # Compose custom event
    custom_event = EventBuilder(Kind(1111), "", []).to_event(keys)

    # Compose text note
    textnote_event = EventBuilder.text_note("Hello", []).to_event(keys)

    # Compose reply to above text note
    reply_event = EventBuilder.text_note("Reply to hello", [Tag.event(textnote_event.id())]).to_event(keys)

    # Compose POW event
    pow_event = EventBuilder.text_note("Another reply with POW", [Tag.event(textnote_event.id())]).to_pow_event(keys, 20)
JavaScript
const { Keys, loadWasmSync, EventBuilder, Tag, Timestamp } = require("@rust-nostr/nostr");

function eventBuilder() {
    // Load WASM
    loadWasmSync();

    let keys = Keys.generate();

    // Compose custom event
    let customEvent = new EventBuilder(1111, "", []).toEvent(keys);

    // Compose text note
    let textnoteEvent = EventBuilder.textNote("Hello", []).toEvent(keys);

    // Compose reply to above text note
    let replyEvent =
        EventBuilder.textNote("Reply to hello", [Tag.parse(["e", textnoteEvent.id.toHex()])])
            .toEvent(keys);

    // Compose POW event
    let powEvent =
        EventBuilder.textNote("Another reply with POW", [Tag.parse(["e", textnoteEvent.id.toHex()])])
            .toPowEvent(keys, 20);

    // Compose note with custom timestamp
    let customTimestamp =
        EventBuilder.textNote("Note with custom timestamp", [])
            .customCreatedAt(Timestamp.fromSecs(12345678))
            .toEvent(keys);
}

module.exports.eventBuilder = eventBuilder;
Kotlin
fun builder() {
    val keys = Keys.generate();

    // Compose custom event
    val customEvent = EventBuilder(Kind(1111u), "", listOf()).toEvent(keys);

    // Compose text note
    val textNoteEvent = EventBuilder.textNote("Hello", listOf()).toEvent(keys);

    // Compose reply to above text note
    val replyEvent = EventBuilder.textNote("Reply to hello", listOf(Tag.event(textNoteEvent.id())))
        .toEvent(keys);

    // Compose POW event
    val powEvent =
    EventBuilder.textNote("Another reply with POW", listOf(Tag.event(textNoteEvent.id())))
        .toPowEvent(keys, 20u);
    println(powEvent.asJson())
}
Swift
import Nostr
import Foundation

func builder() {
    // TODO
}

Event ID

Kind

Timestamp

Tag

Messages

Client Message

Filter

Relay Message

Serialize/deserialize to/from JSON

Rust
use nostr::prelude::*;

pub fn relay_message() -> Result<()> {
    // Deserialize from json
    let json = r#"["EVENT", "random_string", {"id":"70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5","pubkey":"379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe","created_at":1612809991,"kind":1,"tags":[],"content":"test","sig":"273a9cd5d11455590f4359500bccb7a89428262b96b3ea87a756b770964472f8c3e87f5d5e64d8d2e859a71462a3f477b554565c4f2f326cb01dd7620db71502"}]"#;
    let msg = RelayMessage::from_json(json)?;

    // Serialize as json
    let json = msg.as_json();
    println!("{json}");

    Ok(())
}
Python

TODO

JavaScript
const { loadWasmSync, RelayMessage } = require("@rust-nostr/nostr");

function relayMessageJson() {
    // Load WASM
    loadWasmSync();

    // Deserialize from json
    let json1 = '["EVENT", "random_string", {"id":"70b10f70c1318967eddf12527799411b1a9780ad9c43858f5e5fcd45486a13a5","pubkey":"379e863e8357163b5bce5d2688dc4f1dcc2d505222fb8d74db600f30535dfdfe","created_at":1612809991,"kind":1,"tags":[],"content":"test","sig":"273a9cd5d11455590f4359500bccb7a89428262b96b3ea87a756b770964472f8c3e87f5d5e64d8d2e859a71462a3f477b554565c4f2f326cb01dd7620db71502"}]'
    let msg = RelayMessage.fromJson(json1)

    // Serialize as json
    let json2 = msg.asJson()
    console.log(json2);
}

module.exports.relayMessageJson = relayMessageJson;
Kotlin

TODO

Swift

TODO

NIPs

NIP-01

The Event struct represents the structure for an event in Nostr. Many of the NIPs define specific content and tags that are required to correctly represent a kind. The nostr crate ships with a set of NIP-specific utilities for working with certain event kinds.

Metadata (NIP-01)

Rust

Use the Metadata struct to deserialize the content of an event into a struct.

    let event = EventBuilder::new(Kind::Metadata, content, vec![]).to_event(&keys)?;
    let metadata = Metadata::from_json(&event.content)?;

If you have an existing metadata object, it can be used with the EventBuilder struct to create an EventBuilder with the metadata already attached.

    let metadata = Metadata::from_json(content)?;
    let event = EventBuilder::metadata(&metadata).to_event(&keys)?;

For documentation on the available struct attributes, check out the Metadata documentation.

Python

TODO

JavaScript

TODO

Kotlin

TODO

Swift

TODO

NIP-05

NIP-06

NIP-07

NIP-19

NIP-21

NIP-44

Rust
use nostr::prelude::*;

pub fn run() -> Result<()> {
    let keys = Keys::generate();
    
    let pk = PublicKey::from_hex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")?;

    let ciphertext = nip44::encrypt(keys.secret_key()?, &pk, "my message", nip44::Version::V2)?;
    println!("Encrypted: {ciphertext}");

    let plaintext = nip44::decrypt(keys.secret_key()?, &pk, ciphertext)?;
    println!("Decrypted: {plaintext}");

    Ok(())
}
Python
from nostr_protocol import Keys, PublicKey, nip44_encrypt, nip44_decrypt, Nip44Version

def nip44():
    keys = Keys.generate()

    pk = PublicKey.from_hex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")

    ciphertext = nip44_encrypt(keys.secret_key(), pk, "my message", Nip44Version.V2)
    print(f"Encrypted: {ciphertext}")

    plaintext = nip44_decrypt(keys.secret_key(), pk, ciphertext)
    print(f"Decrypted: {plaintext}")
JavaScript
const { Keys, PublicKey, nip44Encrypt, nip44Decrypt, NIP44Version, loadWasmSync } = require("@rust-nostr/nostr");

function run() {
    loadWasmSync();

    let keys = Keys.generate();
    
    let public_key = PublicKey.fromHex("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798");

    let ciphertext = nip44Encrypt(keys.secretKey, public_key, "my message", NIP44Version.V2)
    console.log("Encrypted: " + ciphertext)

    let plaintext = nip44Decrypt(keys.secretKey, public_key, ciphertext)
    console.log("Decrypted: " + plaintext)
}

module.exports.run = run;
Kotlin

TODO

Swift
import Nostr
import Foundation

func nip44() {
    // TODO
}

NIP-59 - Gift Wrap

https://github.com/nostr-protocol/nips/blob/master/59.md

Rust
use nostr::prelude::*;

pub fn run() -> Result<()> {
    // Sender keys
    let alice_keys = Keys::parse("5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a")?;

    // Receiver Keys
    let bob_keys = Keys::parse("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")?;

    // Compose rumor
    let rumor: UnsignedEvent = EventBuilder::text_note("Test", []).to_unsigned_event(alice_keys.public_key());

    // Build gift wrap with sender keys
    let gw: Event = EventBuilder::gift_wrap(&alice_keys, &bob_keys.public_key(), rumor, None)?;
    println!("Gift Wrap: {}", gw.as_json());

    // Extract rumor from gift wrap with receiver keys
    let UnwrappedGift { sender, rumor } = nip59::extract_rumor(&bob_keys, &gw)?;
    println!("Sender: {sender}");
    println!("Rumor: {}", rumor.as_json());

    Ok(())
}
Python
from nostr_protocol import Keys, EventBuilder, Event, gift_wrap, UnwrappedGift, UnsignedEvent


def nip59():
    # Sender Keys
    alice_keys = Keys.parse("5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a")

    # Receiver Keys
    bob_keys = Keys.parse("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")

    # Compose rumor
    rumor = EventBuilder.text_note("Test", []).to_unsigned_event(alice_keys.public_key())

    # Build gift wrap with sender keys
    gw: Event = gift_wrap(alice_keys, bob_keys.public_key(), rumor, None)
    print(f"Gift Wrap: {gw.as_json()}")

    # Extract rumor from gift wrap with receiver keys
    unwrapped_gift = UnwrappedGift.from_gift_wrap(bob_keys, gw)
    sender = unwrapped_gift.sender()
    rumor: UnsignedEvent = unwrapped_gift.rumor()
    print(f"Sender: {sender.to_bech32()}")
    print(f"Rumor: {rumor.as_json()}")
JavaScript
const { Keys, EventBuilder, UnwrappedGift, loadWasmSync } = require("@rust-nostr/nostr");

function run() {
    loadWasmSync();

    // Sender Keys
    const alice_keys = Keys.parse("5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a")

    // Receiver Keys
    const bob_keys = Keys.parse("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99")

    // Compose rumor
    const rumor = EventBuilder.textNote("Test", []).toUnsignedEvent(alice_keys.publicKey)

    // Build gift wrap with sender keys
    const gw = EventBuilder.giftWrap(alice_keys, bob_keys.publicKey, rumor)
    console.log("Gift Wrap: " + gw.asJson())

    // Extract rumor from gift wrap with receiver keys
    let unwrapped_gift = UnwrappedGift.fromGiftWrap(bob_keys, gw);
    console.log("Sender: ", unwrapped_gift.sender.toBech32())
    console.log("Rumor: ", unwrapped_gift.rumor.asJson())
}

module.exports.run = run;
Kotlin

TODO

Swift
import Nostr
import Foundation

func nip59() {
    // TODO
}

NIP-65

Nostr SDK

This section include documentation for the nostr-sdk library for all the supported languages (Rust and bindings).

This library depends on nostr library so, before continue, take a look to the nostr docs.

If you're writing a typical Nostr client or bot, this is likely the library you need.

However, the library is designed in a modular way and depends on several other lower-level crates. If you're attempting something more custom, you might be interested in nostr library.

Installing the library

Rust

Add the nostr-sdk dependency in your Cargo.toml file:

[dependencies]
nostr-sdk = "0.30"

Alternatively, you can add it directly from git source:

[dependencies]
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", tag = "v0.30.0" }

Info

To use a specific commit, use rev instead of tag.

Import the library in your code:

#![allow(unused)]
fn main() {
use nostr_sdk::prelude::*;
}
Python

The nostr-sdk package is available on the public PyPI:

pip install nostr-sdk 

Alternatively, you can manually add the dependency in your requrements.txt, setup.py, etc.:

nostr-sdk==0.11.0

Import the library in your code:

from nostr_sdk import *

Support matrix

The wheels are distributed for the following python versions and platforms. If your version/platform is not currently supported, you can compile the wheel by your self following these instructions.

Python version

3.83.93.103.113.123.13

Platform support

OSi686x64aarch64arm
Linux
macOS
Windows
JavaScript

The nostr-sdk package is available on the public npmjs:

npm i @rust-nostr/nostr-sdk

Alternatively, you can manually add the dependency in your package.json file:

{
    "dependencies": {
        "@rust-nostr/nostr-sdk": "0.13.0"
    }
}

WASM

This library to work require to load the WASM code.

Load in async context

const { loadWasmAsync } = require("@rust-nostr/nostr-sdk");

async function main() {
    // Load WASM
    await loadWasmAsync();

    // ...
}

main();

Load in sync context

const { loadWasmSync } = require("@rust-nostr/nostr-sdk");

function main() {
    // Load WASM
    loadWasmSync();

    // ...
}

main();
Kotlin

To use the Kotlin language bindings for nostr-sdk in your Android project add the following to your gradle dependencies:

repositories {
    mavenCentral()
}

dependencies { 
    implementation("io.github.rust-nostr:nostr-sdk:0.11.0")
}

Import the library in your code:

import rust.nostr.protocol.*
import rust.nostr.sdk.*

Known issues

JNA dependency

Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be

class file for com.sun.jna.Pointer not found

The solution is to add JNA as a dependency like so:

dependencies {
    // ...
    implementation("net.java.dev.jna:jna:5.12.0@aar")
}
Swift

Xcode

Via File > Add Packages..., add

https://github.com/rust-nostr/nostr-sdk-swift.git

as a package dependency in Xcode.

Swift Package

Add the following to the dependencies array in your Package.swift:

.package(url: "https://github.com/rust-nostr/nostr-sdk-swift.git", from: "0.11.0"),

Quickstart

Rust

Create a client and connect to some relays.

    let my_keys: Keys = Keys::generate();

    let client = Client::new(&my_keys);
    let proxy = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050)));

    client.add_relay("wss://relay.damus.io").await?;
    client
        .add_relay_with_opts(
            "wss://relay.nostr.info",
            RelayOptions::new().proxy(proxy).flags(RelayServiceFlags::default().remove(RelayServiceFlags::WRITE)),
        )
        .await?;
    client
        .add_relay_with_opts(
            "ws://jgqaglhautb4k6e6i2g34jakxiemqp6z4wynlirltuukgkft2xuglmqd.onion",
            RelayOptions::new().proxy(proxy),
        )
        .await?;

    client.connect().await;

Add metadata for the keys in the existing client.

    let metadata = Metadata::new()
        .name("username")
        .display_name("My Username")
        .about("Description")
        .picture(Url::parse("https://example.com/avatar.png")?)
        .banner(Url::parse("https://example.com/banner.png")?)
        .nip05("username@example.com")
        .lud16("yuki@getalby.com")
        .custom_field("custom_field", "my value");

    client.set_metadata(&metadata).await?;

Create a filter and notify the relays of the subscription.

    let filter = Filter::new().kind(Kind::Metadata);
    let sub_id: SubscriptionId = client.subscribe(vec![filter], None).await;

For more supported filters, view the documentation.

Listen for notifications from the relays based on the subscribed filters and process them some way.

    let mut notifications = client.notifications();
    while let Ok(notification) = notifications.recv().await {
        if let RelayPoolNotification::Event { subscription_id, event, .. } = notification {
            if subscription_id == sub_id && event.kind == Kind::Metadata {
                // handle the event
                break; // Exit
            }
        }
    }
Python

TODO

JavaScript

TODO

Kotlin

TODO

Swift

TODO

Options

TODO

Proxy

TODO

Donate to Rust Nostr 💜

Your donation directly supports the continued development of rust-nostr!

Give a one-time donation

Bitcoin

Altcoins

We not accept coins different from bitcoin. If you are interested to support rust-nostr with these ones, are available some swap services to convert them directly into bitcoin:

Disclaimer: rust-nostr is not affiliated in any way with these services!

Recurring donations

Please consider becoming a sustaining supporter of the rust-nostr project by giving a recurring monthly donation. If we know how much funding to expect every month, we can better plan our efforts and the use of available resources.

You can setup an automatically recurring donation here:

Verify donation details

The donation details can be verified via the rust-nostr/nostr repository, specifically in the fund directory.