Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Keys and signers

Every event is attributed to a public key and authorized by a signature. The SDK separates the value that identifies an author from the mechanism that produces that signature:

  • PublicKey identifies an author and is safe to distribute.
  • SecretKey is private key material.
  • Keys contains a matching pair and signs directly.
  • a signer provides a public key and signatures without exposing its secret key.

The examples use Keys to keep signing visible. Use the signer interface when another component or device owns the secret.

Generate and parse an identity

The following code creates a temporary identity, encodes its public key as bech32, and parses it again.

Rust
use nostr::prelude::*;

fn keys_example() -> Result<(), Box<dyn std::error::Error>> {
    let keys = Keys::generate();
    let public_key = keys.public_key();
    let encoded = public_key.to_bech32()?;
    let parsed = PublicKey::parse(&encoded)?;

    assert_eq!(parsed, public_key);
    println!("Public key: {encoded}");
    Ok(())
}
Python
from nostr_sdk import Event, EventBuilder, Filter, Kind, KindStandard, Keys, PublicKey, Timestamp

def keys_example() -> None:
    keys = Keys.generate()
    public_key = keys.public_key()
    encoded = public_key.to_bech32()
    parsed = PublicKey.parse(encoded)

    assert parsed == public_key
    print(f"Public key: {encoded}")
JavaScript
Node.js
import {
    Event,
    EventBuilder,
    Filter,
    Kind,
    KindStandard,
    Keys,
    PublicKey,
    Timestamp,
} from "@nostrdevkit/nostr-sdk-node";

function keysExample() {
    const keys = Keys.generate();
    const publicKey = keys.publicKey();
    const encoded = publicKey.toBech32();
    const parsed = PublicKey.parse(encoded);

    console.log("Public key:", parsed.toBech32());
}
Web
import {
    Event,
    EventBuilder,
    Filter,
    Kind,
    KindStandard,
    Keys,
    PublicKey,
    Timestamp,
    uniffiInitAsync,
} from "@nostrdevkit/nostr-sdk-web";

await uniffiInitAsync();

function keysExample() {
    const keys = Keys.generate();
    const publicKey = keys.publicKey();
    const encoded = publicKey.toBech32();
    const parsed = PublicKey.parse(encoded);

    console.log("Public key:", parsed.toBech32());
}
React Native
import {
    Event,
    EventBuilder,
    Filter,
    Kind,
    KindStandard,
    Keys,
    PublicKey,
    Timestamp,
} from "@nostrdevkit/nostr-sdk-react-native";

export function keysExample() {
    const keys = Keys.generate();
    const publicKey = keys.publicKey();
    const encoded = publicKey.toBech32();
    const parsed = PublicKey.parse(encoded);

    console.log("Public key:", parsed.toBech32());
}
Kotlin
import org.nostrdevkit.sdk.*

fun keysExample() {
    val keys = Keys.generate()
    val publicKey = keys.publicKey()
    val encoded = publicKey.toBech32()
    val parsed = PublicKey.parse(encoded)

    check(parsed == publicKey)
    println("Public key: $encoded")
}
Swift
import NostrSDK

func keysExample() throws {
    let keys = Keys.generate()
    let publicKey = keys.publicKey()
    let encoded = try publicKey.toBech32()
    let parsed = try PublicKey.parse(publicKey: encoded)

    print("Public key: \(try parsed.toBech32())")
}
C#
using Nostr.Sdk;

public static class CoreExample
{

    public static void KeysExample()
    {
        var keys = Keys.Generate();
        var publicKey = keys.PublicKey();
        var encoded = publicKey.ToBech32();
        var parsed = PublicKey.Parse(encoded);

        Console.WriteLine($"Public key: {parsed.ToBech32()}");
    }
}

Keys.generate creates a new identity every time. Use it for tests or account creation, not when an existing identity is expected.

Public-key parsing accepts the supported hex and bech32 forms. Keep the parsed PublicKey as a typed value inside the application and encode it only at input or output boundaries.

Keys or signer

Keys signs locally. The signer interface covers hardware, remote signers, browser extensions, and other external implementations. In both cases the SDK flow is the same:

EventBuilder --> unsigned event --> signer --> Event --> Client

External signing can be asynchronous, rejected, or cancelled. Once signed, the resulting Event is independent of the signer and can be passed to the client.

With an identity available, the next chapter constructs the value it signs: an event.