Tools for developing Nostr clients.
Go to file
Luis Miguel 61f397463d nip05 supports uppercase
nip05 says `NIP-05 assumes the <local-part> part will be restricted to the characters a-z0-9-_., case insensitive`

So a lot of people is starting the names with uppercase. See here:

`https://nostr-check.com/.well-known/nostr.json`

So I think we should change the regex to accept lowercase or uppercase.

Another way to do it would be to do a `.toLowerCase` at the beginning, but then we would need to do this search ignoring the case:

```
if (!res?.names?.[name])
```

So maybe for now this is enough?
2023-01-31 10:22:11 -03:00
.github/workflows test every pull request. 2023-01-20 17:00:48 -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 Update README.md 2023-01-14 08:20:10 -03:00
build.js remove useless readable-stream dependency. 2022-12-21 16:19:59 -03:00
event.test.js fix signing functions to be more strict and correct. 2022-12-29 18:26:28 -03:00
event.ts fix signing functions to be more strict and correct. 2022-12-29 18:26:28 -03:00
fakejson.test.js refactor previous commit a little, add fakejson module for simple parsing that doesn't use regex. 2023-01-22 10:32:33 -03:00
fakejson.ts refactor previous commit a little, add fakejson module for simple parsing that doesn't use regex. 2023-01-22 10:32:33 -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 fast insert-into-sorted-list utils. 2023-01-28 18:07:14 -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 replace two packages with a @scure dependency that already existed. 2022-12-27 11:35:21 -03:00
nip05.test.js nip05.queryProfile() and test. 2022-12-20 18:36:49 -03:00
nip05.ts nip05 supports uppercase 2023-01-31 10:22:11 -03:00
nip06.test.js test(nip06): add nip06 2023-01-17 08:15:55 -03:00
nip06.ts feat(nip06): add passphrase optional param 2023-01-17 08:15:55 -03:00
nip19.test.js add nip19. 2022-12-20 18:26:30 -03:00
nip19.ts increase bech32 max size to 5000. 2023-01-18 17:31:37 -03:00
nip26.test.js implement nip26 delegation. 2022-12-23 17:30:35 -03:00
nip26.ts implement nip26 delegation. 2022-12-23 17:30:35 -03:00
package.json add fast insert-into-sorted-list utils. 2023-01-28 18:07:14 -03:00
relay.test.js fix signing functions to be more strict and correct. 2022-12-29 18:26:28 -03:00
relay.ts refactor previous commit a little, add fakejson module for simple parsing that doesn't use regex. 2023-01-22 10:32:33 -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.test.js add fast insert-into-sorted-list utils. 2023-01-28 18:07:14 -03:00
utils.ts add fast insert-into-sorted-list utils. 2023-01-28 18:07:14 -03:00

README.md

nostr-tools

Tools for developing Nostr clients.

Only depends on @scure and @noble packages.

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',
  pubkey: getPublicKey(privateKey)
}

event.id = getEventHash(event)
event.sig = signEvent(event, privateKey)

let ok = validateEvent(event)
let veryOk = 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 = 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, message)

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)
})

Performing and checking for delegation

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

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

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

// generate delegation
let delegation = nip26.createDelegation(sk1, {
  pubkey: pk2,
  kind: 1,
  since: Math.round(Date.now() / 1000),
  until: Math.round(Date.now() / 1000) + 60 * 60 * 24 * 30 /* 30 days */
})

// the delegatee uses the delegation when building an event
let event = {
  pubkey: pk2,
  kind: 1,
  created_at: Math.round(Date.now() / 1000),
  content: 'hello from a delegated key',
  tags: [['delegation', delegation.from, delegation.cond, delegation.sig]]
}

// finally any receiver of this event can check for the presence of a valid delegation tag
let delegator = nip26.getDelegator(event)
assert(delegator === pk1) // will be null if there is no delegation tag or if it is invalid

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.