
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
nostr-effect
Advanced tools
A type-safe Nostr library built with Effect.
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.
bun add nostr-effect
# or
npm install nostr-effect
200+ exports covering the full Nostr protocol:
docs/SUPPORTED_NIPS.md (single source of truth)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()
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 }
})
nostr-effect/pureKey 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/nip19Bech32 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/poolSimplePool 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/relaySingle 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/nip04Legacy 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/nip05DNS-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('bob@example.com')
if (profile) {
console.log('Pubkey:', profile.pubkey)
console.log('Relays:', profile.relays)
}
// Verify an identifier matches a pubkey
const valid = await isValid(pubkey, 'bob@example.com')
// Search for users on a domain
const users = await searchDomain('example.com', 'bob')
nostr-effect/kindsEvent 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/utilsHelper 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")
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/servicesimport {
CryptoService, // Key generation, schnorr signing
EventService, // Event creation and validation
Nip44Service, // NIP-44 encryption
} from "nostr-effect/services"
nostr-effect/clientimport {
Nip17Service, // Private direct messages
// ... other client services
} from "nostr-effect/client"
nostr-effect/relay-serverimport {
// Relay server implementation
RelayServer, PolicyPipeline, NipRegistry, EventStore, SubscriptionManager
} from "nostr-effect/relay-server"
See the definitive support list: docs/SUPPORTED_NIPS.md.
CC0-1.0
FAQs
A type-safe, composable Nostr library built with Effect
The npm package nostr-effect receives a total of 3 weekly downloads. As such, nostr-effect popularity was classified as not popular.
We found that nostr-effect demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.