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
andSwift
so you can buildnostr
apps in your preferred programming language. -
Multi-Platform Support: Write nostr apps for
desktop
,server
,mobile
,web
and/orembedded
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 unparalleledperformance
andmemory safety
, our libraries offersspeed
,stability
andreliability
. The same features are extended to itsbindings
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
Communication
It's available a Matrix space with some rooms at #rustnostr:matrix.org.
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
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" }
Import the library in your code:
use nostr::prelude::*;
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.8 | 3.9 | 3.10 | 3.11 | 3.12 | 3.13 |
---|---|---|---|---|---|
❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
Platform support
OS | i686 | x64 | aarch64 | arm |
---|---|---|---|---|
Linux | ❌ | ✅ | ✅ | ❌ |
macOS | ❌ | ✅ | ✅ | ❌ |
Windows | ❌ | ✅ | ❌ | ❌ |
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();
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")
}
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.1"),
Import the library in your code:
import Nostr
Keys
Generate new random keys
To generate a new key pair use the generate()
method:
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(())
}
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()}")
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());
}
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()}");
}
import Nostr
import Foundation
func keys() {
// TODO
}
Restore from hex and/or bech32 secret key
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(())
}
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)
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);
}
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)
}
TODO
Generate vanity keys
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(())
}
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()}")
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());
}
fun vanity() {
val keys = Keys.vanity(listOf("yuk0"), true, 4u)
println("Public key: ${keys.publicKey().toBech32()}");
println("Secret key: ${keys.secretKey().toHex()}");
}
import Nostr
import Foundation
func vanity() {
// TODO
}
Event
Serialize/deserialize to/from JSON
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(())
}
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}")
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;
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())
}
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.
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(())
}
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)
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;
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())
}
import Nostr
import Foundation
func builder() {
// TODO
}
Event ID
Kind
Timestamp
Tag
Messages
Client Message
Filter
Relay Message
Serialize/deserialize to/from JSON
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(())
}
TODO
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;
TODO
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)
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.
TODO
TODO
TODO
TODO
NIP-05
NIP-06
NIP-07
NIP-19
NIP-21
NIP-44
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(())
}
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}")
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;
TODO
import Nostr
import Foundation
func nip44() {
// TODO
}
NIP-59 - Gift Wrap
https://github.com/nostr-protocol/nips/blob/master/59.md
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(())
}
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()}")
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;
TODO
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
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" }
Import the library in your code:
#![allow(unused)] fn main() { use nostr_sdk::prelude::*; }
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.8 | 3.9 | 3.10 | 3.11 | 3.12 | 3.13 |
---|---|---|---|---|---|
❌ | ✅ | ✅ | ✅ | ✅ | ❌ |
Platform support
OS | i686 | x64 | aarch64 | arm |
---|---|---|---|---|
Linux | ❌ | ✅ | ✅ | ❌ |
macOS | ❌ | ✅ | ✅ | ❌ |
Windows | ❌ | ✅ | ❌ | ❌ |
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();
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")
}
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.1"),
Quickstart
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
}
}
}
TODO
TODO
TODO
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
- 🔗 On-chain:
bc1quk478kpm45744q5pt3p9j42fnv72ykytmt3z0j
- ⚡ Lightning:
yuki@getalby.com
(https://getalby.com/p/yuki)rustnostr@geyser.fund
(https://geyser.fund/project/rustnostr - 4% Geyser fee)
- 🕸️ Fedimint:
TODO
- 💧 Liquid:
TODO
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.