Tools for developing Nostr clients.
Go to file
fiatjaf f7e510e1c8
nip05 regex name check.
2022-12-23 15:04:24 -03:00
.github/workflows fix autopublishing to npm. 2022-12-20 20:15:43 -03:00
.eslintrc.json remove browserify-cipher, use crypto.subtle for nip04. 2022-12-21 16:04:00 -03:00
.gitignore better publishing built files. 2022-12-20 16:56:05 -03:00
.prettierrc.yaml update secp256k1 library, add nip04.js 2021-12-10 21:41:05 -03:00
README.md make relay.connect() an awaitable thing. 2022-12-22 08:53:40 -03:00
build.js remove useless readable-stream dependency. 2022-12-21 16:19:59 -03:00
event.test.js better publishing built files. 2022-12-20 16:56:05 -03:00
event.ts remove buffer usage everywhere. 2022-12-21 16:04:09 -03:00
filter.test.js better publishing built files. 2022-12-20 16:56:05 -03:00
filter.ts Add limit to filter 2022-12-23 11:29:38 -03:00
index.ts add nip19. 2022-12-20 18:26:30 -03:00
keys.test.js better publishing built files. 2022-12-20 16:56:05 -03:00
keys.ts remove buffer usage everywhere. 2022-12-21 16:04:09 -03:00
nip04.test.js make crypto available as a global on nip04 test. 2022-12-21 17:12:50 -03:00
nip04.ts remove browserify-cipher, use crypto.subtle for nip04. 2022-12-21 16:04:00 -03:00
nip05.test.js nip05.queryProfile() and test. 2022-12-20 18:36:49 -03:00
nip05.ts nip05 regex name check. 2022-12-23 15:04:24 -03:00
nip06.ts Don't expose external API to hex representation of mnemoic 2022-12-23 11:01:10 -03:00
nip19.test.js add nip19. 2022-12-20 18:26:30 -03:00
nip19.ts remove browserify-cipher, use crypto.subtle for nip04. 2022-12-21 16:04:00 -03:00
package.json make relay.connect() an awaitable thing. 2022-12-22 08:53:40 -03:00
relay.test.js we need websocket polyfill on relay tests. 2022-12-21 17:09:00 -03:00
relay.ts make relay.connect() an awaitable thing. 2022-12-22 08:53:40 -03:00
tsconfig.json fix typescript types everywhere, delete pool.js and refactor relay.js to use event listeners everywhere. 2022-12-18 17:02:19 -03:00
utils.ts remove browserify-cipher, use crypto.subtle for nip04. 2022-12-21 16:04:00 -03:00

README.md

nostr-tools

Tools for developing Nostr clients.

Very lean on dependencies.

Usage

Generating a private key and a public key

import {generatePrivateKey, getPublicKey} from 'nostr-tools'

let sk = generatePrivateKey() // `sk` is a hex string
let pk = getPublicKey(sk) // `pk` is a hex string

Creating, signing and verifying events

import {
  validateEvent,
  verifySignature,
  signEvent,
  getEventHash,
  getPublicKey
} from 'nostr-tools'

let event = {
  kind: 1,
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  content: 'hello'
}

event.id = getEventHash(event)
event.pubkey = getPublicKey(privateKey)
event.sig = await signEvent(event, privateKey)

let ok = validateEvent(event)
let veryOk = await verifySignature(event)

Interacting with a relay

import {
  relayInit,
  generatePrivateKey,
  getPublicKey,
  getEventHash,
  signEvent
} from 'nostr-tools'

const relay = relayInit('wss://relay.example.com')
await relay.connect()

relay.on('connect', () => {
  console.log(`connected to ${relay.url}`)
})
relay.on('error', () => {
  console.log(`failed to connect to ${relay.url}`)
})

// let's query for an event that exists
let sub = relay.sub([
  {
    ids: ['d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027']
  }
])
sub.on('event', event => {
  console.log('we got the event we wanted:', event)
})
sub.on('eose', () => {
  sub.unsub()
})

// let's publish a new event while simultaneously monitoring the relay for it
let sk = generatePrivateKey()
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,
  pubkey: pk,
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  content: 'hello world'
}
event.id = getEventHash(event)
event.sig = await signEvent(event, sk)

let pub = relay.publish(event)
pub.on('ok', () => {
  console.log(`{relay.url} has accepted our event`)
})
pub.on('seen', () => {
  console.log(`we saw the event on {relay.url}`)
})
pub.on('failed', reason => {
  console.log(`failed to publish to {relay.url}: ${reason}`)
})

await relay.close()

To use this on Node.js you first must install websocket-polyfill and import it:

import 'websocket-polyfill'

Querying profile data from a NIP-05 address

import {nip05} from 'nostr-tools'

let profile = await nip05.queryProfile('jb55.com')
console.log(profile.pubkey)
// prints: 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245
console.log(profile.relays)
// prints: [wss://relay.damus.io]

To use this on Node.js 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, generatePrivateKey, getPublicKey} from 'nostr-tools'

let sk = generatePrivateKey()
let nsec = nip19.nsecEncode(sk)
let {type, data} = nip19.decode(nsec)
assert(type === 'nsec')
assert(data === sk)

let pk = getPublicKey(generatePrivateKey())
let npub = nip19.npubEncode(pk)
let {type, data} = nip19.decode(npub)
assert(type === 'npub')
assert(data === pk)

let pk = getPublicKey(generatePrivateKey())
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)

Encrypting and decrypting direct messages

import {nip04, getPublicKey, generatePrivateKey} from 'nostr-tools'

// sender
let sk1 = generatePrivateKey()
let pk1 = getPublicKey(sk1)

// receiver
let sk2 = generatePrivateKey()
let pk2 = getPublicKey(sk2)

// on the sender side
let message = 'hello'
let ciphertext = await nip04.encrypt(sk1, pk2, 'hello')

let event = {
  kind: 4,
  pubkey: pk1,
  tags: [['p', pk2]],
  content: ciphertext,
  ...otherProperties
}

sendEvent(event)

// on the receiver side
sub.on('event', (event) => {
  let sender = event.tags.find(([k, v]) => k === 'p' && && v && v !== '')[1]
  pk1 === sender
  let plaintext = await nip04.decrypt(sk2, pk1, event.content)
})

Please consult the tests or the source code for more information that isn't available here.

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.generatePrivateKey('...') // and so on
</script>

License

Public domain.