diff --git a/README.md b/README.md index 033c125..018db64 100644 --- a/README.md +++ b/README.md @@ -4,108 +4,105 @@ Tools for developing [Nostr](https://github.com/fiatjaf/nostr) clients. ## Usage +### Generating a private key and a public key + ```js -import {relayPool} from 'nostr-tools' +import { generatePrivateKey, getPublicKey } from 'nostr-tools' -const pool = relayPool() - -pool.setPrivateKey('') // optional - -pool.addRelay('ws://some.relay.com', {read: true, write: true}) -pool.addRelay('ws://other.relay.cool', {read: true, write: true}) - -// example callback functions for listeners -// callback functions take an object argument with following keys: -// - relay: relay url -// - type: type of listener -// - id: sub id for sub specific listeners ('EVENT' or 'EOSE') -// - event: event object, only for 'event' listener -// - notice: notice message, only for 'notice' listener -function onEvent({event, relay, type, id}) { - console.log(`got an event from ${relay} which is already validated.`, event) -} -function onEose({relay, type, id}) { /* callback function here */} -function onNotice({relay, type, notice}) { /* callback function here */} -function onConnection({relay, type}) { /* callback function here */} - -// listen for messages for pool -pool.on('event', onEvent) -pool.on('connection', onConnection) -pool.on('notice', onNotice) - -// subscribing to a single user -// author is the user's public key -pool.sub({filter: {author: ''}}) - -// or bulk follow -pool.sub({filter: {authors: ['', '', ..., '']}}) - -// reuse a subscription channel -const mySubscription = pool.sub({filter: ...., skipVerification: false, beforeSend: ....}) -mySubscription.sub({filter: ....}) -mySubscription.sub({skipVerification: true}) - -// listen for messages for subscription -mySubscription.on('event', onEvent) -mySubscription.on('eose', onEose) - -// close subscription -mySubscription.unsub() - -// get specific event -const specificChannel = pool.sub({ filter: {id: ''}}) - .on('event', ({event, relay}) => { - console.log('got specific event from relay', event, relay) - specificChannel.unsub() - }) - -// or get a specific event plus all the events that reference it in the 'e' tag -pool.sub({ filter: [{id: ''}, {'#e': ''}] }) - -// get all events -pool.sub({ filter: {} }) - -// get recent events -pool.sub({ filter: {since: timestamp} }) - -// publishing events(inside an async function): -const ev = await pool.publish(eventObject, (status, url) => { - if (status === 0) { - console.log(`publish request sent to ${url}`) - } - if (status === 1) { - console.log(`event published by ${url}`, ev) - } -}) -// it will be signed automatically with the key supplied above -// or pass an already signed event to bypass this - -// subscribing to a new relay -pool.addRelay('') -// will automatically subscribe to the all the events called with .sub above +let sk = generatePrivateKey() # `sk` is a hex string +let pk = getPublicKey(sk) # `pk` is a hex string ``` -All functions expect bytearrays as hex strings and output bytearrays as hex strings. +### Creating, signing and verifying events -For other utils please read the source (for now). +```js +const { + validateEvent, + verifySignature, + signEvent, + getEventHash, + getPublicKey +} = require('./cjs') + +let event = { + kind: 1, + created_at: Math.floor(Date.now() / 1000), + tags: [], + content: 'hello' +} + +event.id = getEventHash(event.id) +event.pubkey = getPublicKey(privateKey) +event.sig = await signEvent(event, privateKey) + +let ok = validateEvent(event) +let veryOk = await verifySignature(event) +``` + +### Validating events received from relays + +```js +const {matchFilters} = require('./cjs') + +let event = {kind: 1, pubkey: 'abcdef...', ...otherProperties} +let ok = matchFilters( + [ + { + kinds: [0, 1, 3, 6], + authors: ['abcdef...', '123456...'] + } + ], + event +) +``` + +### Encrypting and decrypting direct messages + +```js +const {nip04, getPublicKey, generatePrivateKey} = require('./cjs') + +// 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 = 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 = nip04.decrypt(sk2, pk1, event.content) +}) +``` + +Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-tools) for more information that isn't available here. ### Using from the browser (if you don't want to use a bundler) -You can import nostr-tools as an ES module. Just add a script tag like this: - ```html - + ``` -And import whatever function you would import from `"nostr-tools"` in a bundler. - -## TypeScript - -This module has hand-authored TypeScript declarations. `npm run check-ts` will run a lint-check script to ensure the typings can be loaded and call at least a few standard library functions. It's not at all comprehensive and likely to contain bugs. Issues welcome; tag @rcoder as needed. - ## License Public domain.