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

nostrmq

v0.4.1

Published

A minimal Node.js message-queue library for encrypted RPC over Nostr

Readme

NostrMQ

A minimal Node.js library for encrypted RPC messaging over the Nostr protocol. NostrMQ provides secure, decentralized message passing with proof-of-work spam prevention and relay pool management.

"NostrMQ lets you build applications like LEGO blocks - choose the best provider for each piece while keeping full control of your identity and data."


Philosophy

Most applications force you into all-or-nothing trust relationships. You either trust a provider with everything, or you self-host.

This limits our design space and options we have to deliver complex systems to people who need them.

NostrMQ uses Nostr like a public message queue to decompose apps into separate components, each run by one or more groups, and links them together through remote RPC.

Each building block can have the trust model that best fits the use case. e.g. let someone else run the client, LLMs etc but keep the signing logic on an isolated device in your house :)

Why Use It?

Don't worry, you don't have to :)

The philosophy and ideas behind it are:

1. User composable apps - Users can choose and swap out components of apps as required. No more "take it or leave it" software with tightly coupled trust models.

2. Sharing the load - Complex systems can be hard to self-host. This lets us share the complexity across both self-hosting and service providers - why not both!

3. Risk sharing - Running Freedom Tech often comes with risk for both server runners and users. The idea is to open up the design space for how we design and share these risks and responsibilities. and introduce more opportunity for....

4. Regulatory arbitrage - Regulation is heavily jurisdiction dependent and risk differs depending on who runs what. This allows you to only run the aspects that are fine in your jurisdiction, then allow users or other service providers to fill in the gaps.

Freedom Technology Principles

For dissidents in authoritarian societies or those dealing with corruption:

  • Operational Security: Keep identity management local while outsourcing heavy computation
  • Plausible Deniability: "I just run an encrypted database, i dont process anything", "I'm only a filesystem" , "I only sign messages in a secure enclave"...
  • Resilience: If one component gets compromised, the others keep running and we can hotswap providers
  • Distributed Risk: No single person carries the full legal/physical risk of the entire system

Features

  • 🔐 Encrypted Messaging: NIP-04 encrypted direct messages
  • Proof-of-Work: Optional spam prevention with configurable difficulty
  • 🌐 Multi-Relay Support: Automatic relay pool management with failover
  • 🔄 Async/Await: Modern Promise-based API with async iteration support
  • 🧵 Multi-threaded PoW: Worker thread support for efficient mining
  • 📦 TypeScript: Full TypeScript support with comprehensive type definitions
  • 🚀 Zero Dependencies: Minimal footprint with only essential dependencies
  • 🛡️ Automatic Replay Protection: Built-in tracking prevents duplicate message processing

🛡️ Automatic Replay Protection

NostrMQ automatically prevents replay attacks by tracking processed messages using a lightweight local cache. This feature works out-of-the-box with zero configuration required.

How It Works

  • Timestamp Filtering: Tracks the timestamp of the last processed message
  • Event ID Tracking: Maintains an in-memory cache of recent event IDs
  • Persistent Storage: Uses .nostrmq/ directory to survive application restarts
  • Smart Filtering: Automatically filters relay subscriptions to only fetch new messages
  • Graceful Fallback: Continues working if file operations fail (memory-only mode)

Performance Metrics

Based on comprehensive testing with 81% test coverage:

  • Processing Rate: 247 events/second
  • Memory Overhead: < 5KB for typical usage
  • File I/O: Minimal (only when timestamp advances)
  • Cache Efficiency: Configurable limits prevent memory bloat

Configuration (Optional)

The tracking system works with sensible defaults, but can be customized via environment variables:

# Override defaults if needed
NOSTRMQ_OLDEST_MQ=3600          # Lookback time in seconds (default: 1 hour)
NOSTRMQ_TRACK_LIMIT=100         # Max recent events to track (default: 100)
NOSTRMQ_CACHE_DIR=.nostrmq      # Cache directory (default: .nostrmq)
NOSTRMQ_DISABLE_PERSISTENCE=false  # Disable file caching (default: false)

Cache Directory Structure

.nostrmq/
├── timestamp.json    # Last processed message timestamp
└── snapshot.json     # Recent event IDs for duplicate detection

Troubleshooting

Cache directory creation fails:

  • System automatically falls back to memory-only mode
  • Check file permissions in your project directory
  • Verify disk space availability

High memory usage:

  • Reduce NOSTRMQ_TRACK_LIMIT to track fewer events
  • Enable NOSTRMQ_DISABLE_PERSISTENCE=true for memory-only mode

Missing old messages:

  • Increase NOSTRMQ_OLDEST_MQ to look further back
  • Clear cache directory to reset tracking state

Installation

npm install nostrmq

Quick Start

1. Environment Setup

Create a .env file with your configuration:

# Required: Your Nostr private key (64 hex characters)
NOSTRMQ_PRIVKEY=your_private_key_here

# Required: Relay URLs (comma-separated). No fallback; must be set.
# Example:
# NOSTR_RELAYS=wss://relay.damus.io,wss://nos.lol,wss://relay.nostr.band
NOSTR_RELAYS=

# Optional: Proof-of-Work settings
NOSTR_POW_DIFFICULTY=0
NOSTR_POW_THREADS=4

2. Basic Usage

import { send, receive } from "nostrmq";

// Send a message
const eventId = await send({
  target: "recipient_pubkey_hex",
  payload: { message: "Hello from NostrMQ!" },
});

// Receive messages
const subscription = receive({
  onMessage: (payload, sender, rawEvent) => {
    console.log("Received:", payload, "from:", sender);
  },
});

// Clean up when done
subscription.close();

API Reference

send(options)

Send an encrypted message to a recipient.

Parameters:

  • options.target (string): Recipient's public key in hex format
  • options.payload (object): Data to send (must be JSON-serializable)
  • options.response (string, optional): Response address (defaults to sender's pubkey)
  • options.relays (string[], optional): Override default relay URLs
  • options.pow (boolean|number, optional): PoW mining configuration
  • options.timeoutMs (number, optional): Connection timeout (default: 2000ms)

Returns: Promise - Event ID of the published message

Example:

const eventId = await send({
  target: "02a1b2c3d4e5f6...",
  payload: {
    type: "greeting",
    message: "Hello!",
    timestamp: new Date().toISOString(),
  },
  pow: 8, // 8-bit proof-of-work
});

receive(options)

Subscribe to incoming encrypted messages.

Parameters:

  • options.onMessage (function): Callback for incoming messages
  • options.relays (string[], optional): Override default relay URLs
  • options.autoAck (boolean, optional): Auto-reply acknowledgment (not implemented)

Returns: SubscriptionHandle with close() method and async iteration support

Example:

// Callback interface
const subscription = receive({
  onMessage: async (payload, sender, rawEvent) => {
    console.log("Message from", sender, ":", payload);
  },
});

// Async iterator interface
for await (const { payload, sender, rawEvent } of subscription) {
  console.log("Received:", payload);
  if (shouldStop) break;
}

subscription.close();

mineEventPow(event, bits, threads?)

Mine proof-of-work for an event template.

Parameters:

  • event (object): Event template to mine
  • bits (number): Target difficulty in leading zero bits
  • threads (number, optional): Number of worker threads (default: 1)

Returns: Promise - Event template with nonce tag added

Example:

const minedEvent = await mineEventPow(eventTemplate, 12, 4);
console.log(
  "Mined with nonce:",
  minedEvent.tags.find((t) => t[0] === "nonce")
);

loadConfig()

Load configuration from environment variables.

Returns: NostrMQConfig object with validated settings

Example:

const config = loadConfig();
console.log("Using pubkey:", config.pubkey);
console.log("Connected to relays:", config.relays);

Advanced Usage

Proof-of-Work Mining

import { send, mineEventPow } from "nostrmq";

// Send with automatic PoW mining
const eventId = await send({
  target: "recipient_pubkey",
  payload: { urgent: true, data: "Important message" },
  pow: 12, // 12-bit difficulty
});

// Manual PoW mining
const eventTemplate = {
  kind: 30072,
  pubkey: "your_pubkey",
  content: "encrypted_content",
  tags: [],
  created_at: Math.floor(Date.now() / 1000),
};

const minedEvent = await mineEventPow(eventTemplate, 8, 4);

Custom Relay Configuration

const customRelays = ["wss://relay.example.com", "wss://another-relay.com"];

// Send to custom relays
await send({
  target: "recipient_pubkey",
  payload: { message: "Hello" },
  relays: customRelays,
});

// Receive from custom relays
const subscription = receive({
  onMessage: (payload, sender) => console.log(payload),
  relays: customRelays,
});

Structured Data Messaging

// Send structured application data
await send({
  target: "recipient_pubkey",
  payload: {
    type: "user_update",
    operation: "profile_change",
    data: {
      userId: 12345,
      name: "Alice Smith",
      email: "[email protected]",
      preferences: {
        notifications: true,
        theme: "dark",
      },
    },
    metadata: {
      version: "1.0",
      source: "user-service",
      timestamp: new Date().toISOString(),
    },
  },
});

Error Handling

try {
  const eventId = await send({
    target: "invalid_pubkey", // This will throw
    payload: { message: "Hello" },
  });
} catch (error) {
  if (error.message.includes("invalid pubkey")) {
    console.error("Invalid recipient public key");
  } else if (error.message.includes("relay")) {
    console.error("Relay connection failed");
  } else {
    console.error("Unexpected error:", error.message);
  }
}

Environment Variables

| Variable | Required | Default | Description | | --------------------- | -------- | ------- | -------------------------------------------------- | | NOSTRMQ_PRIVKEY | Yes | - | Your Nostr private key (64 hex chars) | | NOSTR_RELAYS | Yes | - | Comma-separated relay URLs (no fallback; required) | | NOSTR_POW_DIFFICULTY| No | 0 | Default PoW difficulty in bits | | NOSTR_POW_THREADS | No | 4 | Worker threads for PoW mining |

Examples

See the examples/ directory for complete working examples:

Troubleshooting

Common Issues

"NOSTRMQ_PRIVKEY environment variable is required"

  • Ensure your .env file contains a valid 64-character hex private key
  • Check that your application is loading environment variables correctly

"Failed to connect to relays"

  • Verify relay URLs are accessible and use wss:// protocol
  • Try different relays if current ones are offline
  • Check your network connection and firewall settings

"PoW mining timeout"

  • Reduce difficulty bits for faster mining
  • Increase the number of worker threads
  • Consider disabling PoW for testing (pow: false)

"Invalid pubkey format"

  • Ensure recipient public keys are 64-character hex strings
  • Verify the pubkey doesn't include prefixes like npub (use hex format)

Performance Tips

  • Use multiple worker threads for PoW mining on multi-core systems
  • Keep PoW difficulty reasonable (8-16 bits) for good performance
  • Reuse subscription handles instead of creating new ones frequently
  • Use connection pooling by keeping relay connections alive

Debugging

Enable debug logging by setting the environment variable:

DEBUG=nostrmq:* node your-app.js

TypeScript Support

NostrMQ includes comprehensive TypeScript definitions:

import { send, receive, SendOpts, ReceiveOpts, NostrMQConfig } from "nostrmq";

const sendOptions: SendOpts = {
  target: "recipient_pubkey",
  payload: { message: "Hello TypeScript!" },
  pow: 8,
};

const eventId: string = await send(sendOptions);

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Make your changes and add tests
  4. Run the build: npm run build
  5. Submit a pull request

License

MIT License - see LICENSE file for details.

Related Projects


NostrMQ - Secure, decentralized messaging for the modern web.