Signers
NDK uses signers optionally passed in to sign events. Note that it is possible to use NDK without signing events ( e.g. to get someone's profile).
Signing adapters can be passed in when NDK is instantiated or later during runtime.
Using a NIP-07 browser extension (e.g. Alby, nos2x)
Instatiate NDK with a NIP-07 signer
// Import the package, NIP-07 signer and NDK event
import NDK, { NDKEvent, NDKNip07Signer } from "@nostr-dev-kit/ndk";
const nip07signer = new NDKNip07Signer();
const ndk = new NDK({ signer: nip07signer });
NDK can now ask for permission, via their NIP-07 extension, to...
Read the user's public key
nip07signer.user().then(async (user) => {
if (!!user.npub) {
console.log("Permission granted to read their public key:", user.npub);
}
});
Sign & publish events
const ndkEvent = new NDKEvent(ndk);
ndkEvent.kind = 1;
ndkEvent.content = "Hello, world!";
ndkEvent.publish(); // This will trigger the extension to ask the user to confirm signing.
Using a Remote Signer (NIP-46)
bunker://
- Create a
NDKNip46Signer
, optionally providing the local signer if you are restoring a connection that was already generated in your app:
const signerConnectionString = 'bunker://....'; // ask the user for the bunker connection string
const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it
const signer = NDKNip46Signer.bunker(ndk, signerConnectionString, localNsec);
const user = await signer.blockUntilReady();
console.log("Welcome", user.npub);
// if you didn't have a localNsec you should store it for future sessions of your app
save(signer.localSigner.nsec)
nostrconnect://
The nostrconnect://
flow is the reverse of the bunker://
flow; the app generates a connection string and is sent to the signer out of band (such as scanning a QR code).
const relay = 'wss://relay.primal.net'; // choose a relay to be used to communicate with the signer
const localNsec = `nsec1....`; // Restore this from whatever storage your app is using, if you have it
// instantiate the signer
const signer = NDKNip46Signer.nostrconnect(ndk, relay, localNsec, {
name: "<your-app-name>",
icon: "<your-app-icon>",
perms: [ 'sign_event:1,sign_event:30023' ] // permissions to request (e.g. sign event kind:1 and kind:30023)
});
// Open the signer or show the signer.nostrConnectUri URI as a QR code
open(signer.nostrConnectUri);
// Wait for the user to connect
const user = await signer.blockUntilReady();
console.log("Welcome", user.npub);
// if you didn't have a localNsec you should store it for future sessions of your app
save(signer.localSigner.nsec)
Using a Private Key Signer
NDK provides NDKPrivateKeySigner
for managing in-memory private keys. This is useful for development, testing, or applications that manage keys locally.
Basic Usage
import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
// Generate a new private key
const signer = NDKPrivateKeySigner.generate();
console.log("nsec:", signer.nsec);
console.log("npub:", signer.npub);
// Or load from an existing key
const signer = new NDKPrivateKeySigner("nsec1...");
Password-Protected Keys (NIP-49)
NDK supports NIP-49 encrypted private keys (ncryptsec format) for secure storage:
import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
// Encrypt a private key with a password
const signer = NDKPrivateKeySigner.generate();
const password = "user-chosen-password";
const ncryptsec = signer.encryptToNcryptsec(password);
// Store ncryptsec securely (e.g., in localStorage)
localStorage.setItem("encrypted_key", ncryptsec);
// Later, restore the signer from encrypted key
const storedKey = localStorage.getItem("encrypted_key");
const restoredSigner = NDKPrivateKeySigner.fromNcryptsec(storedKey, password);
console.log("Restored pubkey:", restoredSigner.pubkey);
Security Parameters:
The encryptToNcryptsec
method accepts optional parameters for security tuning:
// Higher security (slower, more resistant to brute force)
const ncryptsec = signer.encryptToNcryptsec(password, 20); // log_n = 20 (~2 seconds, 1GB memory)
// Default security (faster)
const ncryptsec = signer.encryptToNcryptsec(password, 16); // log_n = 16 (~100ms, 64MB memory)
// With key security byte (0x00, 0x01, or 0x02)
const ncryptsec = signer.encryptToNcryptsec(password, 16, 0x02);
Direct NIP-49 utilities:
NDK also re-exports NIP-49 utilities for advanced use cases:
import { nip49 } from "@nostr-dev-kit/ndk";
import { hexToBytes, bytesToHex } from "@noble/hashes/utils";
// Encrypt raw bytes
const privateKeyBytes = hexToBytes("14c226dbdd865d5e...");
const ncryptsec = nip49.encrypt(privateKeyBytes, password);
// Decrypt to raw bytes
const decryptedBytes = nip49.decrypt(ncryptsec, password);