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 🙏

© 2025 – Pkg Stats / Ryan Hefner

nostr-deploy-server

v2.0.2

Published

Node.js server for hosting static websites under npub subdomains using Nostr protocol and Blossom servers

Readme

Nostr Deploy - Server Implementation

A Node.js server implementation for serving static websites under npub subdomains using the Nostr protocol and Blossom servers. This server implements the Pubkey Static Websites NIP specification, allowing decentralized hosting of static websites without storing any data locally.

🌟 Features

  • Decentralized Hosting: Serves websites using Nostr events (kind 34128) for path mappings and Blossom servers for file storage

  • Npub Subdomains: Supports npub1xxx.example.com wildcard subdomain routing

  • Zero Storage: Acts as a pure proxy/gateway without storing any files locally

  • Advanced Multi-Layer Caching: Sophisticated caching system with multiple backends (In-Memory, Redis, SQLite), intelligent cache invalidation, TTL management, and comprehensive monitoring tools for optimal performance

  • Real-Time Cache Invalidation: Proactive pre-caching system that updates cache entries immediately when content is published to Nostr, ensuring zero-latency responses for users

  • Automatic Fallbacks: Falls back to /404.html for missing files and default servers when user configurations are unavailable

  • Rate Limiting: Configurable rate limiting to prevent abuse

  • Graceful Shutdown: Proper cleanup of connections and resources

🚀 Key Innovation: Real-Time Pre-Caching

This server's real-time cache invalidation system sets it apart from traditional static hosting solutions:

  • 📡 Nostr Event Monitoring: Continuously monitors Nostr relays for content updates
  • ⚡ Zero-Latency Responses: Content is cached before users visit, not after
  • 🔄 Instant Updates: Cache refreshes immediately when content is published to Nostr
  • 🎯 Smart Pre-Loading: Automatically caches file mappings, relay lists, and server preferences

Traditional Flow: User visits → Query Nostr → Wait 500-2000ms → Serve content
Our Flow: Content published → Instant cache update → User visits → Serve in 5-50ms

📋 NIP Implementation

This server implements the Pubkey Static Websites NIP which allows:

  1. Static File Events (Kind 34128): Maps file paths to SHA256 hashes

    {
      "kind": 34128,
      "tags": [
        ["d", "/index.html"],
        ["x", "186ea5fd14e88fd1ac49351759e7ab906fa94892002b60bf7f5a428f28ca1c99"]
      ]
    }
  2. Relay Lists (Kind 10002): User's preferred Nostr relays (NIP-65)

  3. Blossom Server Lists (Kind 10063): User's preferred Blossom servers (BUD-03)

🚀 Quick Start

  1. Install dependencies:

    npm install
  2. Configure environment variables:

    cp env.example .env
    # Edit .env with your configuration
  3. Run in development mode:

    npm run dev
  4. Build and run in production:

    npm run build
    npm start

🔧 Advanced Caching System

This server features a sophisticated multi-layer caching system inspired by modern NoSQL and distributed caching patterns. The caching system supports multiple backends and provides significant performance improvements.

Cache Backends

The server supports three cache backends:

  1. In-Memory Cache (Default)

    CACHE_PATH=in-memory
  2. Redis Cache (Recommended for production)

    CACHE_PATH=redis://localhost:6379
    # or with authentication:
    CACHE_PATH=redis://username:password@localhost:6379/0
  3. SQLite Cache (Good for single-instance deployments)

    CACHE_PATH=sqlite:///path/to/cache.db

Cache Layers

The caching system includes multiple specialized cache layers:

  • Domain Resolution Cache: Maps domain names to pubkeys
  • Blossom Servers Cache: Caches available blossom servers per pubkey
  • Relay Lists Cache: Caches relay lists per pubkey
  • Path Mapping Cache: Maps file paths to blob metadata
  • Blob URLs Cache: Caches available URLs for each blob
  • File Content Cache: Caches actual file content
  • Negative Cache: Caches "not found" results to avoid repeated lookups

Real-Time Cache Invalidation

The server features a sophisticated real-time cache invalidation system that proactively updates cache entries as content is published to Nostr, ensuring users always get the latest content without waiting for cache expiration.

How It Works

  1. Nostr Event Subscription: The server subscribes to configured Nostr relays and monitors for relevant events
  2. Event Processing: When events are published, the cache is immediately updated with new information
  3. Pre-Caching: Content is cached before users visit the site, ensuring zero-latency responses

Monitored Event Types

  • Kind 34128: Static file events (file path → SHA256 mappings)
  • Kind 10002: Relay list events (user's preferred Nostr relays)
  • Kind 10063: Blossom server list events (user's preferred file servers)

Benefits

  • Zero-Latency Responses: Files are already cached when users visit
  • Always Fresh Content: Cache updates immediately when content is published
  • Reduced Load: Fewer queries to Nostr relays during user requests
  • Better User Experience: Instant page loads for published content

Configuration

Enable real-time cache invalidation in your .env file:

# Enable real-time cache invalidation (default: true)
REALTIME_CACHE_INVALIDATION=true

# Relays to monitor for cache invalidation events
INVALIDATION_RELAYS=wss://relay.primal.net,wss://relay.damus.io,wss://relay.nostr.band

# Timeouts for invalidation connections
INVALIDATION_TIMEOUT_MS=30000
INVALIDATION_RECONNECT_DELAY_MS=5000

Example Workflow

  1. Publisher: User publishes /index.html → SHA256 mapping (Kind 34128) to Nostr
  2. Server: Immediately receives event and updates cache with new mapping
  3. User: Visits npub1xyz.example.com/index.html → Gets instant response from cache
  4. Result: Zero-latency response because content was pre-cached

Cache Configuration

Add these environment variables to your .env file:

# Advanced Caching Configuration
CACHE_PATH=in-memory          # or redis://... or sqlite://...
CACHE_TIME=3600              # Default TTL in seconds (1 hour)

Testing the Cache System

Run the comprehensive cache test suite:

# Test all cache functionality
npm run test:cache

# Run all tests including cache tests
npm test

# Run tests in watch mode
npm run test:watch

Complete Documentation

For comprehensive documentation on the caching system, see: 📖 Caching Documentation

This includes:

  • Detailed architecture overview
  • Complete API reference
  • Performance benchmarks
  • Monitoring and troubleshooting guides
  • Production deployment strategies

Setting Up Redis Cache Backend

Redis is the recommended cache backend for production deployments as it provides persistence, horizontal scaling, and high performance.

Installing Redis

macOS (using Homebrew):

# Install Redis
brew install redis

# Start Redis service
brew services start redis

# Or start manually
redis-server

Ubuntu/Debian:

# Update package list
sudo apt update

# Install Redis
sudo apt install redis-server

# Start Redis service
sudo systemctl start redis-server
sudo systemctl enable redis-server

# Check Redis status
sudo systemctl status redis-server

Docker (Recommended for development):

# Run Redis in Docker
docker run -d --name redis-cache -p 6379:6379 redis:alpine

# With persistence
docker run -d --name redis-cache \
  -p 6379:6379 \
  -v redis-data:/data \
  redis:alpine redis-server --appendonly yes

CentOS/RHEL/Rocky Linux:

# Install EPEL repository
sudo dnf install epel-release

# Install Redis
sudo dnf install redis

# Start and enable Redis
sudo systemctl start redis
sudo systemctl enable redis

Redis Configuration

Create a Redis configuration for production:

# Create Redis config directory
sudo mkdir -p /etc/redis

# Create configuration file
sudo tee /etc/redis/redis.conf << EOF
# Network configuration
bind 127.0.0.1
port 6379
protected-mode yes

# Memory configuration
maxmemory 256mb
maxmemory-policy allkeys-lru

# Persistence configuration
save 900 1
save 300 10
save 60 10000

# Security
requirepass your-secure-password-here

# Logging
loglevel notice
logfile /var/log/redis/redis-server.log

# Performance
tcp-keepalive 300
timeout 0
EOF

# Restart Redis with new config
sudo systemctl restart redis

Environment Configuration for Redis

Add to your .env file:

# Basic Redis connection
CACHE_PATH=redis://localhost:6379

# Redis with password
CACHE_PATH=redis://:your-password@localhost:6379

# Redis with username and password (Redis 6+)
CACHE_PATH=redis://username:password@localhost:6379

# Redis with database selection
CACHE_PATH=redis://localhost:6379/0

# Redis with custom connection options
CACHE_PATH=redis://localhost:6379?retry_delay=100&max_attempts=3

# Redis cluster or remote server
CACHE_PATH=redis://redis.example.com:6379

Redis Monitoring

Monitor Redis performance:

# Connect to Redis CLI
redis-cli

# Monitor commands in real-time
redis-cli monitor

# Get Redis info
redis-cli info

# Check memory usage
redis-cli memory usage

# List all keys (use with caution in production)
redis-cli keys "*"

# Get cache statistics
redis-cli info stats

Setting Up SQLite Cache Backend

SQLite is perfect for single-instance deployments or when you want persistent caching without setting up a Redis server.

Installing SQLite

SQLite comes pre-installed on most systems, but you can install it if needed:

macOS:

# Usually pre-installed, or install via Homebrew
brew install sqlite

Ubuntu/Debian:

sudo apt update
sudo apt install sqlite3

CentOS/RHEL:

sudo dnf install sqlite

SQLite Configuration

SQLite requires minimal configuration. Just specify the database file path:

# Environment configuration for SQLite
CACHE_PATH=sqlite:///var/lib/nostr-deploy/cache.db

# Or relative path
CACHE_PATH=sqlite://./data/cache.db

# In-memory SQLite (loses data on restart)
CACHE_PATH=sqlite://:memory:

Creating SQLite Cache Directory

# Create cache directory
sudo mkdir -p /var/lib/nostr-deploy
sudo chown $(whoami):$(whoami) /var/lib/nostr-deploy

# Or for local development
mkdir -p ./data

SQLite Performance Tuning

For better SQLite performance, create a configuration script:

// SQLite performance optimization (optional)
// Add to your application startup code

import Database from 'better-sqlite3';

if (process.env.CACHE_PATH?.startsWith('sqlite://')) {
  const dbPath = process.env.CACHE_PATH.replace('sqlite://', '');
  const db = new Database(dbPath);

  // Performance optimizations
  db.exec(`
    PRAGMA journal_mode = WAL;
    PRAGMA synchronous = NORMAL;
    PRAGMA cache_size = 1000000;
    PRAGMA temp_store = memory;
    PRAGMA mmap_size = 268435456;
  `);

  db.close();
}

SQLite Monitoring

Monitor SQLite database:

# Connect to SQLite database
sqlite3 /var/lib/nostr-deploy/cache.db

# List tables
.tables

# Get database info
.dbinfo

# Check database size
.dbconfig

# Analyze database performance
ANALYZE;

# Vacuum database (optimize storage)
VACUUM;

Cache Backend Comparison

| Feature | In-Memory | Redis | SQLite | | -------------------- | ---------------- | ------------ | ----------------- | | Performance | Fastest | Very Fast | Fast | | Persistence | None | Yes | Yes | | Scalability | Single Process | Horizontal | Single Process | | Memory Usage | High | Configurable | Low | | Setup Complexity | None | Medium | Low | | Production Ready | Development Only | Yes | Small-Medium Apps | | Network Overhead | None | Low | None |

Cache Performance Benchmarks

Typical performance characteristics:

  • In-Memory: ~1-2ms response time, 100,000+ ops/sec
  • Redis: ~2-5ms response time, 50,000+ ops/sec
  • SQLite: ~5-10ms response time, 10,000+ ops/sec

Troubleshooting Cache Issues

Redis Connection Issues:

# Test Redis connection
redis-cli ping

# Check Redis logs
sudo tail -f /var/log/redis/redis-server.log

# Verify Redis is listening
sudo netstat -tlnp | grep 6379

SQLite Permission Issues:

# Fix file permissions
sudo chown -R $(whoami):$(whoami) /var/lib/nostr-deploy/
chmod 755 /var/lib/nostr-deploy/
chmod 644 /var/lib/nostr-deploy/cache.db

Memory Issues:

# Check Redis memory usage
redis-cli info memory

# Monitor system resources
htop
df -h

Performance Benefits

The advanced caching system provides:

  • 3-5x faster response times for cached requests
  • Reduced load on Nostr relays through intelligent caching
  • Lower bandwidth usage with blob URL availability caching
  • Improved reliability with negative caching for failed lookups
  • Horizontal scaling support with Redis backend

⚙️ Configuration

Configure the server using environment variables or create a .env file:

# Server Configuration
PORT=3000
NODE_ENV=production

# Domain Configuration
BASE_DOMAIN=example.com

# Default Nostr Relays (comma-separated)
DEFAULT_RELAYS=wss://relay.nostr.band,wss://nostrue.com,wss://purplerelay.com,wss://relay.primal.net,wss://nos.lol,wss://relay.damus.io,wss://relay.nsite.lol

# Default Blossom Servers (comma-separated)
DEFAULT_BLOSSOM_SERVERS=https://cdn.hzrd149.com,https://blossom.primal.net,https://blossom.band,https://loratu.bitcointxoko.com,https://blossom.f7z.io,https://cdn.sovbit.host

# Caching Configuration
CACHE_TTL_SECONDS=300
MAX_CACHE_SIZE=100

# Rate Limiting
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100

# Security & Performance
CORS_ORIGIN=*
TRUST_PROXY=false
REQUEST_TIMEOUT_MS=30000
MAX_FILE_SIZE_MB=50
LOG_LEVEL=info

SSL Certificate Setup

For production deployment, you need a wildcard SSL certificate:

# Example with Let's Encrypt/Certbot
certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
  -d "*.example.com" \
  -d "example.com"

# Update .env with certificate paths
WILDCARD_CERT_PATH=/etc/letsencrypt/live/example.com/fullchain.pem
WILDCARD_KEY_PATH=/etc/letsencrypt/live/example.com/privkey.pem

🌐 How It Works

1. Request Flow with Advanced Caching

sequenceDiagram
    participant Client
    participant Server
    participant Cache as Cache Layer
    participant Nostr as Nostr Relays
    participant Blossom as Blossom Servers

    Client->>Server: GET npub123.example.com/index.html
    Server->>Server: Resolve npub to pubkey

    Server->>Cache: Check domain resolution cache
    alt Cache Hit
        Cache-->>Server: Return cached pubkey mapping
    else Cache Miss
        Server->>Nostr: Fetch relay list (kind 10002)
        Server->>Cache: Store relay list
    end

    Server->>Cache: Check path mapping cache
    alt Cache Hit
        Cache-->>Server: Return cached file mapping
    else Cache Miss
        Server->>Nostr: Fetch file mapping (kind 34128)
        Server->>Cache: Store path mapping
    end

    Server->>Cache: Check Blossom servers cache
    alt Cache Hit
        Cache-->>Server: Return cached server list
    else Cache Miss
        Server->>Nostr: Fetch Blossom servers (kind 10063)
        Server->>Cache: Store Blossom servers
    end

    Server->>Cache: Check file content cache
    alt Cache Hit
        Cache-->>Server: Return cached file content
        Server->>Client: Serve cached file
    else Cache Miss
        Server->>Blossom: Fetch file by SHA256
        Server->>Cache: Store file content
        Server->>Client: Serve file with headers
    end

2. Path Resolution

  • //index.html
  • /blog//blog/index.html
  • /about/about/index.html (if no extension)
  • Missing files → /404.html fallback

3. Advanced Multi-Layer Caching Strategy

The server implements a sophisticated caching system with multiple specialized layers:

Cache Layers & TTL Configuration

  • Domain Resolution Cache: Maps npub subdomains to pubkeys (TTL: 1 hour)
  • Relay Lists Cache: User's preferred Nostr relays (kind 10002) (TTL: 5 minutes)
  • Path Mapping Cache: File path to SHA256 mappings (kind 34128) (TTL: 5 minutes)
  • Blossom Servers Cache: User's preferred Blossom servers (kind 10063) (TTL: 5 minutes)
  • Blob URLs Cache: Available URLs for each blob across servers (TTL: 10 minutes)
  • File Content Cache: Actual file content with compression (TTL: 30 minutes)
  • Negative Cache: "Not found" results to avoid repeated lookups (TTL: 1 minute)

Cache Backends Support

  1. In-Memory Cache (Development)

    • Fastest performance (~1-2ms response time)
    • No persistence, data lost on restart
    • Suitable for single-instance development
  2. Redis Cache (Production Recommended)

    • High performance (~2-5ms response time)
    • Persistent storage with configurable eviction policies
    • Horizontal scaling support with clustering
    • Advanced features: pub/sub, transactions, Lua scripting
  3. SQLite Cache (Small/Medium Production)

    • Good performance (~5-10ms response time)
    • File-based persistence with ACID transactions
    • Zero-configuration setup
    • Perfect for single-instance deployments

Intelligent Cache Invalidation

  • Real-Time Updates: Subscribes to Nostr relays and updates cache immediately when content is published
  • Pre-Caching: Content is cached before users visit, ensuring zero-latency responses
  • Event-Driven: Monitors Kind 34128 (file mappings), 10002 (relay lists), and 10063 (blossom servers)
  • Time-based Expiration: Configurable TTL per cache layer
  • Dependency Tracking: Cascading invalidation for related cache entries
  • Manual Cache Control: Administrative endpoints for cache management
  • Smart Prefetching: Predictive caching based on access patterns

Cache Performance Features

  • LRU Eviction: Automatic removal of least recently used entries
  • Memory Management: Configurable memory limits with overflow protection
  • Compression: Gzip compression for file content cache
  • Batch Operations: Efficient bulk cache operations
  • Connection Pooling: Optimized Redis/SQLite connection management
  • Circuit Breakers: Automatic fallback when cache backend is unavailable

Monitoring & Debugging

  • Cache Hit/Miss Metrics: Real-time performance statistics
  • Memory Usage Tracking: Detailed memory consumption monitoring
  • Debug Logging: Comprehensive cache operation logging
  • Health Checks: Cache backend health monitoring endpoints
  • Performance Profiling: Request-level cache performance analysis

🔌 API Endpoints

Static File Serving

GET *

Main endpoint that serves static files based on npub subdomain routing.

🏗️ Architecture

Project Structure

/
├── src/
│   ├── types/           # TypeScript type definitions
│   ├── utils/           # Utility classes (config, logger, cache)
│   ├── helpers/         # Core helpers (Nostr, Blossom)
│   ├── __tests__/       # Test files
│   └── server.ts        # Main server application
├── dist/                # Compiled JavaScript (generated)
├── package.json
├── tsconfig.json
├── jest.config.js
└── README.md

Core Components

  • ConfigManager: Environment configuration and validation
  • Logger: Structured logging with Winston
  • MemoryCache: TTL-based in-memory caching
  • NostrHelper: Nostr relay communication and event parsing
  • BlossomHelper: Blossom server file fetching
  • Express Server: HTTP request handling and routing

🧪 Testing

The server includes comprehensive test coverage:

# Run all tests
npm test

# Run specific test files
npm test -- config.test.ts
npm test -- nostr.test.ts

# Run with coverage
npm test -- --coverage

# Watch mode for development
npm run test:watch

Test Categories

  • Unit Tests: Individual component testing
  • Integration Tests: Component interaction testing
  • Mock Tests: External service simulation
  • Configuration Tests: Environment validation

🚀 Production Deployment

Docker Deployment

FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY dist/ ./dist/
COPY .env ./

EXPOSE 3000
CMD ["node", "dist/server.js"]

Reverse Proxy Configuration

Nginx

server {
    listen 443 ssl http2;
    server_name *.example.com;

    ssl_certificate /path/to/wildcard.crt;
    ssl_certificate_key /path/to/wildcard.key;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Apache

<VirtualHost *:443>
    ServerName *.example.com
    ServerAlias *.example.com

    SSLEngine on
    SSLCertificateFile /path/to/wildcard.crt
    SSLCertificateKeyFile /path/to/wildcard.key

    ProxyPass / http://localhost:3000/
    ProxyPassReverse / http://localhost:3000/
    ProxyPreserveHost On
</VirtualHost>

Process Management

PM2

# Install PM2
npm install -g pm2

# Start with PM2
pm2 start dist/server.js --name nostr-deploy-server

# Setup auto-restart
pm2 startup
pm2 save

Systemd

[Unit]
Description=Nostr Deploy Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/nostr-deploy-server
ExecStart=/usr/bin/node dist/server.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

🔧 Troubleshooting

Common Issues

  1. Invalid npub subdomains returning 404

    • Check BASE_DOMAIN configuration
    • Verify wildcard DNS setup
    • Ensure npub format is correct
  2. Files not loading from Blossom servers

    • Check DEFAULT_BLOSSOM_SERVERS configuration
    • Verify network connectivity to Blossom servers
    • Check server logs for specific error messages
  3. Slow response times

    • Increase CACHE_TTL_SECONDS
    • Increase MAX_CACHE_SIZE
    • Check network latency to relays/Blossom servers
  4. Rate limiting issues

    • Adjust RATE_LIMIT_MAX_REQUESTS
    • Adjust RATE_LIMIT_WINDOW_MS
    • Implement IP whitelisting if needed

Debug Mode

# Enable debug logging
LOG_LEVEL=debug npm start

# Check if server is running
curl http://localhost:3000

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure all tests pass: npm test
  5. Submit a pull request

Development Guidelines

  • Follow TypeScript best practices
  • Maintain test coverage above 80%
  • Use meaningful commit messages
  • Update documentation for new features

📄 License

MIT License - see LICENSE file for details.

🔗 Related Projects

🙋‍♂️ Support

  • Create an issue for bug reports
  • Check existing issues before creating new ones
  • Join the Nostr community for protocol discussions