nostr-tools
Tools for developing Nostr clients.
Only depends on @scure and @noble packages.
This package is only providing lower-level functionality. If you want an easy-to-use fully-fledged solution that abstracts the hard parts of Nostr and makes decisions on your behalf, take a look at NDK and @snort/system.
Installation
npm install nostr-tools
If using TypeScript, this package requires TypeScript >= 5.0.
Usage
Generating a private key and a public key
import { generateSecretKey, getPublicKey } from 'nostr-tools'
let sk = generateSecretKey()
let pk = getPublicKey(sk)
Creating, signing and verifying events
import { finalizeEvent, verifyEvent } from 'nostr-tools'
let event = finalizeEvent({
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'hello',
}, sk)
let isGood = verifyEvent(event)
Interacting with a relay
import { relayConnect, finalizeEvent, generateSecretKey, getPublicKey } from 'nostr-tools'
const relay = await relayConnect('wss://relay.example.com')
console.log(`connected to ${relay.url}`)
const sub = relay.subscribe([
{
ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027'],
},
], {
onevent(event) {
console.log('we got the event we wanted:', event)
},
oneose() {
sub.close()
}
})
let sk = generateSecretKey()
let pk = getPublicKey(sk)
let sub = relay.sub([
{
kinds: [1],
authors: [pk],
},
])
sub.on('event', event => {
console.log('got event:', event)
})
let event = {
kind: 1,
created_at: Math.floor(Date.now() / 1000),
tags: [],
content: 'hello world',
}
const signedEvent = finalizeEvent(event, sk)
await relay.publish(signedEvent)
let events = await relay.list([{ kinds: [0, 1] }])
let event = await relay.get({
ids: ['44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'],
})
relay.close()
To use this on Node.js you first must install websocket-polyfill
and import it:
import 'websocket-polyfill'
Interacting with multiple relays
import { SimplePool } from 'nostr-tools'
const pool = new SimplePool()
let relays = ['wss://relay.example.com', 'wss://relay.example2.com']
let h = pool.subscribeMany(
[...relays, 'wss://relay.example3.com'],
[
{
authors: ['32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'],
},
],
{
onevent(event) {
},
oneose() {
h.close()
}
}
)
await Promise.any(pool.publish(relays, newEvent))
console.log('published to at least one relay!')
let events = await pool.querySync(relays, [{ kinds: [0, 1] }])
let event = await pool.get(relays, {
ids: ['44e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245'],
})
Parsing references (mentions) from a content using NIP-10 and NIP-27
import { parseReferences } from 'nostr-tools'
let references = parseReferences(event)
let simpleAugmentedContent = event.content
for (let i = 0; i < references.length; i++) {
let { text, profile, event, address } = references[i]
let augmentedReference = profile
? `<strong>@${profilesCache[profile.pubkey].name}</strong>`
: event
? `<em>${eventsCache[event.id].content.slice(0, 5)}</em>`
: address
? `<a href="${text}">[link]</a>`
: text
simpleAugmentedContent.replaceAll(text, augmentedReference)
}
Querying profile data from a NIP-05 address
import { nip05 } from 'nostr-tools'
let profile = await nip05.queryProfile('jb55.com')
console.log(profile.pubkey)
console.log(profile.relays)
To use this on Node.js < v18, you first must install node-fetch@2
and call something like this:
nip05.useFetchImplementation(require('node-fetch'))
Encoding and decoding NIP-19 codes
import { nip19, generateSecretKey, getPublicKey } from 'nostr-tools'
let sk = generateSecretKey()
let nsec = nip19.nsecEncode(sk)
let { type, data } = nip19.decode(nsec)
assert(type === 'nsec')
assert(data === sk)
let pk = getPublicKey(generateSecretKey())
let npub = nip19.npubEncode(pk)
let { type, data } = nip19.decode(npub)
assert(type === 'npub')
assert(data === pk)
let pk = getPublicKey(generateSecretKey())
let relays = ['wss://relay.nostr.example.mydomain.example.com', 'wss://nostr.banana.com']
let nprofile = nip19.nprofileEncode({ pubkey: pk, relays })
let { type, data } = nip19.decode(nprofile)
assert(type === 'nprofile')
assert(data.pubkey === pk)
assert(data.relays.length === 2)
Import modes
Using just the packages you want
Importing the entirety of nostr-tools
may bloat your build, so you should probably import individual packages instead:
import { generateSecretKey, finalizeEvent, verifyEvent } from 'nostr-tools/pure'
import { matchFilter } from 'nostr-tools/filter'
import { decode, nprofileEncode, neventEncode, npubEncode } from 'nostr-tools/nip19'
Using it with nostr-wasm
nostr-wasm
is a thin wrapper over libsecp256k1 compiled to WASM just for hashing, signing and verifying Nostr events.
import { setNostrWasm, generateSecretKey, finalizeEvent, verifyEvent } from 'nostr-tools/wasm'
import { initNostrWasm } from 'nostr-wasm'
initNostrWasm().then(setNostrWasm)
This may be faster than the pure-JS noble libraries used by default and in nostr-tools/pure
.
Using from the browser (if you don't want to use a bundler)
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
<script>
window.NostrTools.generateSecretKey('...')
</script>
Plumbing
To develop nostr-tools
, install just
and run just -l
to see commands available.
License
This is free and unencumbered software released into the public domain. By submitting patches to this project, you agree to dedicate any and all copyright interest in this software to the public domain.