npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nostr-effect

v0.0.12

Published

A type-safe, composable Nostr library built with Effect

Downloads

47

Readme

nostr-effect

A type-safe Nostr library built with Effect.

Why

We want the entire Nostr protocol—client library and relay—implemented in fully typed Effect TypeScript. This gives us composable error handling, dependency injection via layers, and structured concurrency out of the box.

Installation

bun add nostr-effect
# or
npm install nostr-effect

What's Included

200+ exports covering the full Nostr protocol:

  • See supported NIPs in docs/SUPPORTED_NIPS.md (single source of truth)
  • Effect Services: CryptoService, EventService, DVMService, Nip05Service, Nip17Service, Nip25Service, Nip39Service, Nip46Service, Nip58Service, RelayService, and more
  • Branded Types: NostrEvent, Filter, PublicKey, SecretKey, EventId, Signature, SubscriptionId, UnixTimestamp
  • NIP-19 Encoding: encode/decode for npub, nsec, note, nprofile, nevent, naddr
  • NIP-06 Keys: Seed word/mnemonic key derivation
  • Relay Server: RelayServer, PolicyPipeline, NipRegistry, EventStore, SubscriptionManager
  • Typed Errors: CryptoError, InvalidSignature, ValidationError, ConnectionError, and more

Quick Start

Promise API

The Promise API provides a simple interface inspired by nostr-tools. Under the hood, it uses the full Effect-based implementation with type-safe services and NIP modules.

Note: The Promise API covers the most common functionality. Full access to all NIPs and services is available via the Effect API.

import { generateSecretKey, getPublicKey, finalizeEvent, verifyEvent } from "nostr-effect/pure"
import { npubEncode, nsecEncode, decode } from "nostr-effect/nip19"
import { SimplePool } from "nostr-effect/pool"

// Generate keys
const sk = generateSecretKey()
const pk = getPublicKey(sk)

// Encode to bech32
const npub = npubEncode(pk)
const nsec = nsecEncode(sk)

// Create and sign an event
const event = finalizeEvent({
  kind: 1,
  created_at: Math.floor(Date.now() / 1000),
  tags: [],
  content: "Hello, Nostr!"
}, sk)

// Verify signature
console.log(verifyEvent(event)) // true

// Query relays
const pool = new SimplePool()
const events = await pool.querySync(
  ["wss://relay.damus.io", "wss://nos.lol"],
  { kinds: [1], limit: 10 }
)
pool.destroy()

Effect API

import { Effect } from "effect"
import { CryptoService, EventService } from "nostr-effect"
import * as Nip19 from "nostr-effect"

const program = Effect.gen(function* () {
  const crypto = yield* CryptoService
  const events = yield* EventService

  // Generate keypair
  const keyPair = yield* crypto.generateKeyPair()

  // Encode to npub
  const npub = Nip19.npubEncode(keyPair.publicKey)

  // Create and sign event
  const event = yield* events.createSignedEvent({
    kind: 1,
    content: "Hello from Effect!",
    tags: []
  }, keyPair.secretKey)

  return { npub, event }
})

API Reference

Promise Wrappers

nostr-effect/pure

Key generation, event signing, and verification.

import {
  generateSecretKey,  // () => Uint8Array
  getPublicKey,       // (sk: Uint8Array) => string
  finalizeEvent,      // (template, sk) => VerifiedEvent
  verifyEvent,        // (event) => boolean
  serializeEvent,     // (event) => string
  getEventHash,       // (event) => string
  validateEvent,      // (event) => boolean
  sortEvents,         // (events) => void
} from "nostr-effect/pure"

nostr-effect/nip19

Bech32 encoding/decoding for Nostr identifiers.

import {
  npubEncode,      // (pubkey: string) => NPub
  nsecEncode,      // (seckey: Uint8Array) => NSec
  noteEncode,      // (eventId: string) => Note
  nprofileEncode,  // ({ pubkey, relays? }) => NProfile
  neventEncode,    // ({ id, relays?, author?, kind? }) => NEvent
  naddrEncode,     // ({ identifier, pubkey, kind, relays? }) => NAddr
  decode,          // (bech32: string) => { type, data }
  decodeNostrURI,  // (uri: string) => { type, data }
  NostrTypeGuard,  // { isNPub, isNSec, isNote, ... }
} from "nostr-effect/nip19"

nostr-effect/pool

SimplePool for managing relay connections.

import { SimplePool } from "nostr-effect/pool"

const pool = new SimplePool()
pool.subscribe(relays, filters, { onevent, oneose })
pool.subscribeIterator(relays, filters)  // AsyncIterable
pool.querySync(relays, filters)          // Promise<Event[]>
pool.get(relays, filters)                // Promise<Event | null>
pool.publish(relays, event)              // Promise<string>[]
pool.destroy()

nostr-effect/relay

Single relay connection (simpler than SimplePool).

import { Relay, connectRelay } from "nostr-effect/relay"

// Create and connect
const relay = new Relay('wss://relay.damus.io')
relay.on('connect', () => console.log('Connected!'))
relay.on('error', (err) => console.error(err))
await relay.connect()

// Or use convenience function
const relay = await connectRelay('wss://relay.damus.io')

// Subscribe to events
const sub = relay.subscribe([{ kinds: [1], limit: 10 }], {
  onevent: (event) => console.log(event),
  oneose: () => console.log('End of stored events'),
})

// Publish an event
await relay.publish(signedEvent)

// Clean up
sub.close()
relay.close()

nostr-effect/nip04

Legacy encrypted DMs (NIP-04).

import { encrypt, decrypt } from "nostr-effect/nip04"

// Encrypt a message (uses shared secret from sender's privkey + receiver's pubkey)
const ciphertext = await encrypt(senderSecretKey, receiverPubkey, "Hello!")

// Decrypt a message
const plaintext = await decrypt(receiverSecretKey, senderPubkey, ciphertext)

nostr-effect/nip05

DNS-based identity verification (NIP-05).

import { queryProfile, isValid, searchDomain, NIP05_REGEX } from "nostr-effect/nip05"

// Look up a user's profile
const profile = await queryProfile('[email protected]')
if (profile) {
  console.log('Pubkey:', profile.pubkey)
  console.log('Relays:', profile.relays)
}

// Verify an identifier matches a pubkey
const valid = await isValid(pubkey, '[email protected]')

// Search for users on a domain
const users = await searchDomain('example.com', 'bob')

nostr-effect/kinds

Event kind constants for all NIPs.

import { kinds } from "nostr-effect/kinds"

// Use kind constants
const event = { kind: kinds.ShortTextNote, ... }  // kind 1

// Check if event is a reaction
if (event.kind === kinds.Reaction) { ... }

// Helper functions
kinds.isReplaceable(kind)              // kind 0, 3, or 10000-19999
kinds.isEphemeral(kind)                // kind 20000-29999
kinds.isParameterizedReplaceable(kind) // kind 30000-39999
kinds.getDVMResultKind(requestKind)    // 5xxx -> 6xxx

nostr-effect/utils

Helper utilities for working with events.

import {
  matchFilter, matchFilters,           // Check if event matches filter(s)
  sortEvents, sortEventsAsc,           // Sort events by timestamp
  normalizeURL,                        // Normalize relay URLs
  getTagValue, getTagValues, getTags,  // Extract tags from events
  deduplicateEvents,                   // Remove duplicate events by ID
  getLatestReplaceable,                // Get latest version of replaceable events
  now, timestampToDate, dateToTimestamp, // Timestamp helpers
} from "nostr-effect/utils"

// Check if an event matches a filter
const matches = matchFilter({ kinds: [1], authors: [pubkey] }, event)

// Sort events newest first
const sorted = sortEvents(events)

// Get tag values
const referencedPubkeys = getTagValues(event, "p")

Effect Services

nostr-effect (main)

import {
  // Services
  CryptoService,    // Key generation, signing, encryption
  EventService,     // Event creation and validation

  // Core types and schemas
  NostrEvent,       // Event schema
  Filter,           // Filter schema
  PublicKey,        // Branded type
  SecretKey,        // Branded type
  EventId,          // Branded type
  Signature,        // Branded type

  // NIP modules (namespaced)
  Nip04,            // Legacy encrypted DMs
  Nip06,            // Key derivation from mnemonic
  Nip11,            // Relay information
  Nip13,            // Proof of Work
  Nip17,            // Private direct messages
  Nip19,            // bech32 encoding (Effect version)
  Nip21,            // nostr: URI scheme
  Nip27,            // Content parsing
  Nip30,            // Custom emoji
  Nip34,            // Git collaboration
  Nip40,            // Expiration timestamp
  Nip42,            // Client authentication
  Nip47,            // Nostr Wallet Connect
  Nip49,            // Encrypted private keys
  Nip54,            // Wiki
  Nip59,            // Gift wrap
  Nip75,            // Zap goals
  Nip94,            // File metadata
  Nip98,            // HTTP auth
  Nip99,            // Classified listings
} from "nostr-effect"

nostr-effect/services

import {
  CryptoService,    // Key generation, schnorr signing
  EventService,     // Event creation and validation
  Nip44Service,     // NIP-44 encryption
} from "nostr-effect/services"

nostr-effect/client

import {
  Nip17Service,     // Private direct messages
  // ... other client services
} from "nostr-effect/client"

nostr-effect/relay-server

import {
  // Relay server implementation
  RelayServer, PolicyPipeline, NipRegistry, EventStore, SubscriptionManager
} from "nostr-effect/relay-server"

NIP Support

See the definitive support list: docs/SUPPORTED_NIPS.md.

License

CC0-1.0