{"id":15729,"library":"nostr-tools","title":"Nostr Client Development Tools","description":"nostr-tools is a core JavaScript/TypeScript library providing low-level utilities for developing Nostr clients. It enables essential functionalities such as generating Nostr secret and public keys, creating and signing events, verifying event integrity, and interacting with Nostr relays through a `SimplePool` abstraction. The current stable version is 2.23.3, and the project appears to have an active release cadence, evidenced by significant breaking changes in version 2.0.0 and subsequent updates. Key differentiators include its minimalist dependency footprint, relying primarily on `@scure` and `@noble` cryptography packages, and its modular structure which allows importing only necessary components. It specifically focuses on lower-level primitives, suggesting `@nostr/gadgets` for higher-level client features. It also provides robust relay management features like configurable pinging and automatic reconnection.","status":"active","version":"2.23.3","language":"javascript","source_language":"en","source_url":"https://github.com/nbd-wtf/nostr-tools","tags":["javascript","decentralization","social","censorship-resistance","client","nostr","typescript"],"install":[{"cmd":"npm install nostr-tools","lang":"bash","label":"npm"},{"cmd":"yarn add nostr-tools","lang":"bash","label":"yarn"},{"cmd":"pnpm add nostr-tools","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency, required for TypeScript users.","package":"typescript","optional":false},{"reason":"Runtime dependency for Node.js environments to provide a WebSocket implementation for SimplePool and Relay.","package":"ws","optional":true}],"imports":[{"note":"ESM-only for modern usage. These are pure functions for cryptographic operations.","wrong":"const { generateSecretKey, getPublicKey } = require('nostr-tools/pure')","symbol":"generateSecretKey, getPublicKey","correct":"import { generateSecretKey, getPublicKey } from '@nostr/tools/pure'"},{"note":"Since v2.0.0, functions are imported from specific subpaths like `/pure` due to modularization. Avoid legacy 'nostr-tools' root import for these functions.","wrong":"import { finalizeEvent } from 'nostr-tools'","symbol":"finalizeEvent, verifyEvent","correct":"import { finalizeEvent, verifyEvent } from '@nostr/tools/pure'"},{"note":"The primary class for relay interaction. Must be imported from the `/pool` subpath since v2.0.0.","wrong":"import { SimplePool } from 'nostr-tools'","symbol":"SimplePool","correct":"import { SimplePool } from '@nostr/tools/pool'"},{"note":"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.","wrong":"import { useWebSocketImplementation } from '@nostr/tools/relay'","symbol":"useWebSocketImplementation","correct":"import { useWebSocketImplementation } from '@nostr/tools/pool'"}],"quickstart":{"code":"import { finalizeEvent, generateSecretKey, getPublicKey } from '@nostr/tools/pure';\nimport { SimplePool, useWebSocketImplementation } from '@nostr/tools/pool';\nimport WebSocket from 'ws'; // For Node.js environments\nimport { bytesToHex } from '@noble/hashes/utils'; // For convenience\n\n// For Node.js, set up the WebSocket implementation\nuseWebSocketImplementation(WebSocket);\n\nasync function runNostrClient() {\n  const pool = new SimplePool({ enablePing: true, enableReconnect: true });\n\n  // Use a real, publicly available relay for testing\n  const relays = ['wss://relay.damus.io', 'wss://nostr.wine', 'wss://eden.nostr.land'];\n\n  // 1. Generate keys\n  let sk = generateSecretKey(); // Uint8Array\n  let pk = getPublicKey(sk); // hex string\n  console.log(`Generated secret key (hex): ${bytesToHex(sk)}`);\n  console.log(`Generated public key (hex): ${pk}`);\n\n  // 2. Create and sign an event\n  let eventTemplate = {\n    kind: 1, // Text Note\n    created_at: Math.floor(Date.now() / 1000),\n    tags: [],\n    content: `Hello Nostr from nostr-tools! This is a test event from a registry quickstart. ${Math.random()}`,\n  };\n  const signedEvent = finalizeEvent(eventTemplate, sk);\n  console.log('Signed event:', signedEvent);\n\n  // 3. Publish the event to a couple of relays\n  console.log(`Publishing event to ${relays.slice(0, 2).join(', ')}...`);\n  try {\n    // Promise.any will resolve as soon as one relay successfully publishes\n    await Promise.any(pool.publish(relays.slice(0, 2), signedEvent));\n    console.log('Event published successfully to at least one relay.');\n  } catch (error) {\n    console.error('Failed to publish event to any relay:', error);\n  }\n\n  // 4. Subscribe to events from our public key\n  console.log(`Subscribing to events from public key ${pk}...`);\n  const sub = pool.subscribe(\n    relays,\n    {\n      kinds: [1],\n      authors: [pk],\n      since: Math.floor(Date.now() / 1000) - 60, // Look for events in the last 60 seconds\n    },\n    {\n      onevent(event) {\n        console.log('Received own event:', event.content);\n        // Once we receive our own event, we can unsubscribe if desired\n        sub.close();\n      },\n      oneose() {\n        console.log('Subscription End of Stored Events (EOSE) received.');\n      },\n      onclose(wasClean: boolean) {\n        console.log(`Subscription closed (wasClean: ${wasClean}).`);\n      }\n    }\n  );\n\n  // Optional: Query for some existing events\n  console.log('Querying for recent text notes (kind 1)...');\n  const recentEvents = await pool.querySync(\n    relays,\n    {\n      kinds: [1],\n      limit: 5,\n    },\n    {\n      onprogress(percent: number) {\n        // console.log(`Query progress: ${Math.round(percent)}%`);\n      }\n    }\n  );\n  if (recentEvents && recentEvents.length > 0) {\n    console.log(`Found ${recentEvents.length} recent events. Example:`, recentEvents[0].content);\n  } else {\n    console.log('No recent events found.');\n  }\n\n  // Allow some time for events to propagate and be received\n  await new Promise(resolve => setTimeout(resolve, 5000));\n\n  pool.close(); // Close all relay connections\n  console.log('Pool closed.');\n}\n\nrunNostrClient().catch(console.error);","lang":"typescript","description":"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."},"warnings":[{"fix":"Review the v2.0.0 release notes and update all imports, method calls, and type usages according to the new API structure and modular import paths. Kinds are now constants, not enums.","message":"Version 2.0.0 introduced significant breaking changes, including API renames, removal of deprecated methods, changes to Event type parameters (e.g., `Event<number>` was removed), and kind constants replacing enums.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Change your import statements from `import { SomeSymbol } from '@nostr/tools'` to `import { SomeSymbol } from '@nostr/tools/subpath'`, where `subpath` is typically `pure`, `pool`, or `relay`.","message":"With v2.0.0, the package transitioned to a modular structure, requiring imports from specific subpaths (e.g., `@nostr/tools/pure`, `@nostr/tools/pool`) instead of the root `@nostr/tools` package for most functionality.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Install the `ws` package (`npm install ws`) and add the following code at the entry point of your Node.js application: `import WebSocket from 'ws'; import { useWebSocketImplementation } from '@nostr/tools/pool'; useWebSocketImplementation(WebSocket);`","message":"When using `SimplePool` or `Relay` in a Node.js environment, you must explicitly provide a WebSocket implementation, typically by installing the `ws` package and calling `useWebSocketImplementation(WebSocket)`.","severity":"gotcha","affected_versions":"*"},{"fix":"Ensure your project's `devDependencies` or `dependencies` include `\"typescript\": \">=5.0.0\"` and update your TypeScript installation if necessary.","message":"The package lists TypeScript >= 5.0.0 as a peer dependency. Using an older version of TypeScript may lead to type incompatibility issues or compilation errors.","severity":"gotcha","affected_versions":"*"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"Install `ws` (`npm install ws`) and call `useWebSocketImplementation(WebSocket)` from `@nostr/tools/pool` (or `/relay`) with the `ws` import.","cause":"Attempting to use `SimplePool` or `Relay` in a Node.js environment without a global WebSocket implementation being set.","error":"TypeError: global.WebSocket is not a constructor"},{"fix":"Provide a WebSocket implementation by installing `ws` and calling `useWebSocketImplementation(WebSocket)` from `@nostr/tools/pool` (or `/relay`).","cause":"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.","error":"ReferenceError: WebSocket is not defined"},{"fix":"Convert 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.","cause":"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.","error":"SyntaxError: Cannot use import statement outside a module"}],"ecosystem":"npm"}