Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslnode 18–226 runs
build_error
glibcnode 18–226 runs
build_error
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
generateSecretKey, getPublicKey
✓ import { generateSecretKey, getPublicKey } from '@nostr/tools/pure'
✗ const { generateSecretKey, getPublicKey } = require('nostr-tools/pure')
ESM-only for modern usage. These are pure functions for cryptographic operations.
finalizeEvent, verifyEvent
✓ import { finalizeEvent, verifyEvent } from '@nostr/tools/pure'
✗ import { finalizeEvent } from 'nostr-tools'
Since v2.0.0, functions are imported from specific subpaths like `/pure` due to modularization. Avoid legacy 'nostr-tools' root import for these functions.
SimplePool
✓ import { SimplePool } from '@nostr/tools/pool'
✗ import { SimplePool } from 'nostr-tools'
The primary class for relay interaction. Must be imported from the `/pool` subpath since v2.0.0.
useWebSocketImplementation
✓ import { useWebSocketImplementation } from '@nostr/tools/pool'
✗ import { useWebSocketImplementation } from '@nostr/tools/relay'
Essential for Node.js environments to explicitly provide a WebSocket implementation (e.g., `ws` package) to the library. Can also be from `/relay` if using `Relay` directly.
Demonstrates generating Nostr keys, creating and signing an event, publishing it to relays, subscribing to events from a public key, and configuring the WebSocket implementation for Node.js.
import { finalizeEvent, generateSecretKey, getPublicKey } from '@nostr/tools/pure';
import { SimplePool, useWebSocketImplementation } from '@nostr/tools/pool';
import WebSocket from 'ws'; // For Node.js environments
import { bytesToHex } from '@noble/hashes/utils'; // For convenience
// For Node.js, set up the WebSocket implementation
useWebSocketImplementation(WebSocket);
async function runNostrClient() {
const pool = new SimplePool({ enablePing: true, enableReconnect: true });
// Use a real, publicly available relay for testing
const relays = ['wss://relay.damus.io', 'wss://nostr.wine', 'wss://eden.nostr.land'];
// 1. Generate keys
let sk = generateSecretKey(); // Uint8Array
let pk = getPublicKey(sk); // hex string
console.log(`Generated secret key (hex): ${bytesToHex(sk)}`);
console.log(`Generated public key (hex): ${pk}`);
// 2. Create and sign an event
let eventTemplate = {
kind: 1, // Text Note
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: `Hello Nostr from nostr-tools! This is a test event from a registry quickstart. ${Math.random()}`,
};
const signedEvent = finalizeEvent(eventTemplate, sk);
console.log('Signed event:', signedEvent);
// 3. Publish the event to a couple of relays
console.log(`Publishing event to ${relays.slice(0, 2).join(', ')}...`);
try {
// Promise.any will resolve as soon as one relay successfully publishes
await Promise.any(pool.publish(relays.slice(0, 2), signedEvent));
console.log('Event published successfully to at least one relay.');
} catch (error) {
console.error('Failed to publish event to any relay:', error);
}
// 4. Subscribe to events from our public key
console.log(`Subscribing to events from public key ${pk}...`);
const sub = pool.subscribe(
relays,
{
kinds: [1],
authors: [pk],
since: Math.floor(Date.now() / 1000) - 60, // Look for events in the last 60 seconds
},
{
onevent(event) {
console.log('Received own event:', event.content);
// Once we receive our own event, we can unsubscribe if desired
sub.close();
},
oneose() {
console.log('Subscription End of Stored Events (EOSE) received.');
},
onclose(wasClean: boolean) {
console.log(`Subscription closed (wasClean: ${wasClean}).`);
}
}
);
// Optional: Query for some existing events
console.log('Querying for recent text notes (kind 1)...');
const recentEvents = await pool.querySync(
relays,
{
kinds: [1],
limit: 5,
},
{
onprogress(percent: number) {
// console.log(`Query progress: ${Math.round(percent)}%`);
}
}
);
if (recentEvents && recentEvents.length > 0) {
console.log(`Found ${recentEvents.length} recent events. Example:`, recentEvents[0].content);
} else {
console.log('No recent events found.');
}
// Allow some time for events to propagate and be received
await new Promise(resolve => setTimeout(resolve, 5000));
pool.close(); // Close all relay connections
console.log('Pool closed.');
}
runNostrClient().catch(console.error);
Errors
Common errors & fixes
TypeError: global.WebSocket is not a constructor
Attempting to use `SimplePool` or `Relay` in a Node.js environment without a global WebSocket implementation being set.
fixInstall `ws` (`npm install ws`) and call `useWebSocketImplementation(WebSocket)` from `@nostr/tools/pool` (or `/relay`) with the `ws` import.
ReferenceError: WebSocket is not defined
Similar to the `TypeError`, this occurs in Node.js when the `SimplePool` or `Relay` tries to instantiate a WebSocket without an available global `WebSocket` constructor.
fixProvide a WebSocket implementation by installing `ws` and calling `useWebSocketImplementation(WebSocket)` from `@nostr/tools/pool` (or `/relay`).
SyntaxError: Cannot use import statement outside a module
The `nostr-tools` library is primarily distributed as an ES Module (ESM). This error occurs when trying to use `import` statements in a CommonJS (CJS) context in Node.js.
fixConvert your Node.js project to use ES Modules by adding `"type": "module"` to your `package.json` and using `.js` files (or `.mjs`). Alternatively, use a bundler (e.g., Webpack, Rollup) for client-side applications.
Audit
Dependencies
typescriptrequiredPeer dependency, required for TypeScript users.
wsoptionalRuntime dependency for Node.js environments to provide a WebSocket implementation for SimplePool and Relay.