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

ai-paywall

v1.0.3

Published

Middleware for protecting routes behind Sui blockchain payments - forces AI agents to pay before accessing premium content

Readme

ai-paywall

Express middleware and client SDK for protecting routes behind Sui blockchain payments with Seal encryption - perfect for forcing AI agents to pay before accessing premium content.

🚀 Quick Start

Installation

npm install ai-paywall

📖 Table of Contents


🖥️ Server-Side Usage

Basic Setup

Protect your routes with just 3 parameters! All contract details are baked into the package.

const express = require('express');
const { paywall } = require('ai-paywall');

const app = express();

// Protect a route - that's it!
app.use('/premium', paywall({
  price: '0.01',                    // Price in SUI
  receiver: '0x...',                // Your wallet address (where payments go)
  domain: 'www.example.com',        // Your domain (must match registry)
}));

// Protected route handler
app.get('/premium', (req, res) => {
  // Middleware has already:
  // ✅ Verified AccessPass on-chain
  // ✅ Fetched encrypted content from Walrus
  // ✅ Stored it in req.paywall.encryptedBlob
  
  res.setHeader('Content-Type', 'application/octet-stream');
  res.send(Buffer.from(req.paywall.encryptedBlob));
});

Setting Up with Registered Content

If you've already registered content in registry-app:

const express = require('express');
const { paywall } = require('ai-paywall');

const app = express();

// Your registered resource configuration
const RESOURCE = {
  domain: 'www.yourdomain.com',
  resource: '/hidden/content',
  price: '0.01',
  receiver: '0x...', // Your wallet address
  resourceEntryId: '0x...', // Optional: speeds up lookup
};

// Protect the route
app.use(RESOURCE.resource, paywall({
  price: RESOURCE.price,
  receiver: RESOURCE.receiver,
  domain: RESOURCE.domain,
  resourceEntryId: RESOURCE.resourceEntryId, // Optional optimization
  // Note: If resourceEntryId not provided, middleware queries registry automatically
}));

// Route handler
app.get(RESOURCE.resource, (req, res) => {
  // Serve encrypted content
  res.setHeader('Content-Type', 'application/octet-stream');
  res.setHeader('X-Resource-Entry-ID', req.paywall.resourceEntry?.resource_id);
  res.send(Buffer.from(req.paywall.encryptedBlob));
});

See example/new-server-example.js for a complete working example.

What the Middleware Does

  1. ✅ Checks for payment headers on every request
  2. ✅ Returns 402 Payment Required if no valid headers
  3. ✅ Verifies AccessPass on Sui blockchain (no database needed!)
  4. ✅ Fetches ResourceEntry from registry by domain/resource
  5. ✅ Downloads encrypted content from Walrus storage
  6. ✅ Stores encrypted blob in req.paywall.encryptedBlob
  7. ✅ Provides ResourceEntry ID via X-Resource-Entry-ID header

Request Object

After middleware verification, req.paywall contains:

req.paywall = {
  accessPass: AccessPass,      // Verified AccessPass object
  verified: true,              // Verification status
  encryptedBlob: ArrayBuffer,  // Encrypted content from Walrus
  resourceEntry: {             // Resource metadata
    domain: string,
    resource: string,
    walrus_cid: string,
    seal_policy: string,
    resource_id: string,       // ResourceEntry object ID
    // ... other fields
  }
}

🤖 Client-Side Usage

Basic Access

The client SDK handles everything automatically: payment detection, AccessPass purchase, authentication, and decryption.

const { PaywallClient } = require('ai-paywall');

// Initialize client
const client = new PaywallClient({
  privateKey: process.env.PRIVATE_KEY,
});

// Access and decrypt in one call
const decrypted = await client.accessAndDecrypt(
  'http://example.com/premium',
  'www.example.com',  // Domain from registry
  '/premium'          // Resource path
  // ResourceEntry ID automatically extracted from server headers!
);

That's it! The client automatically:

  • ✅ Detects 402 payment required
  • ✅ Purchases AccessPass if needed
  • ✅ Signs authentication headers
  • ✅ Makes authenticated request
  • ✅ Extracts ResourceEntry ID from server headers
  • ✅ Decrypts content using Seal

Step-by-Step Access (Manual Control)

const { PaywallClient } = require('ai-paywall');

const client = new PaywallClient({
  privateKey: process.env.PRIVATE_KEY,
});

// Step 1: Access protected route (gets encrypted blob)
const encryptedBlob = await client.access('http://example.com/premium');

// Step 2: Find AccessPass (if needed for decryption)
const accessPassId = await client.findExistingAccessPass('www.example.com', '/premium');

// Step 3: Decrypt content
const decrypted = await client.decrypt(
  encryptedBlob,
  resourceEntryId,  // Get from server's X-Resource-Entry-ID header
  accessPassId
);

See example/new-client-example.js for a complete working example.


📝 Examples

Server Example

cd example
export WALRUS_DOMAIN=www.yourdomain.com
export WALRUS_RESOURCE=/your/resource/path
export RECEIVER_ADDRESS=0x...
node new-server-example.js

Client Example

cd example
export PRIVATE_KEY=your-private-key
export SERVER_URL=http://localhost:3000
export WALRUS_DOMAIN=www.yourdomain.com
export WALRUS_RESOURCE=/your/resource/path
node new-client-example.js

See example/README.md for more details.


⚙️ Configuration

Middleware Options

interface PaywallOptions {
  price: string;           // Price in SUI (e.g., "0.01")
  receiver: string;        // Your wallet address (where payments go)
  domain: string;          // Domain name (must match registry exactly)
  resourceEntryId?: string; // Optional: ResourceEntry ID (optimization cache)
  mockContent?: string;     // Optional: Mock content for testing
}

Client Options

interface PaywallClientOptions {
  privateKey: string;      // Base64 or hex private key
  rpcUrl?: string;         // Optional: Sui RPC URL (default: testnet)
}

🔄 How It Works

Request Flow

1. Client Request (no headers)
   ↓
2. Middleware → 402 Payment Required
   ↓
3. Client Purchases AccessPass (on-chain)
   ↓
4. Client Signs Headers (x-pass-id, x-sig, etc.)
   ↓
5. Client Retries Request (with headers)
   ↓
6. Middleware Verifies AccessPass (on-chain)
   ↓
7. Middleware Fetches ResourceEntry (from registry)
   ↓
8. Middleware Fetches Encrypted Content (from Walrus)
   ↓
9. Middleware Returns Encrypted Blob + Headers
   ↓
10. Client Decrypts Content (using Seal)

Response Codes

  • 402 Payment Required: No payment headers or invalid pass
  • 403 Forbidden: Invalid pass, expired, or no remaining uses
  • 200 OK: Access granted (encrypted blob in response)

Client Headers

Clients must include these headers when accessing protected routes:

| Header | Description | Example | |--------|-------------|---------| | x-pass-id | AccessPass object ID | 0x1234... | | x-signer | Owner address (signer) | 0xabcd... | | x-sig | Signature (base64) | signature... | | x-ts | Timestamp (ms) | 1704067200000 |


🏗️ Architecture

For detailed technical information about the system architecture, components, and data flows, see:

Key Components

  1. Registry App - Content registration and encryption
  2. Paywall Middleware - Access control and content delivery
  3. PaywallClient - Payment and content access SDK
  4. Sui Blockchain - AccessPass and registry storage
  5. Walrus Storage - Decentralized encrypted content storage
  6. Seal Encryption - Zero-knowledge encryption/decryption

❓ Troubleshooting

"Resource not found in registry"

  • ✅ Check domain matches registry exactly (case-sensitive)
  • ✅ Check route path matches resource exactly
  • ✅ Verify ResourceEntry exists on Sui blockchain
  • ✅ Try providing resourceEntryId in middleware options (optional optimization)

"AccessPass not found"

  • ✅ Client needs to purchase AccessPass first
  • ✅ AccessPass must match domain/resource
  • ✅ Check AccessPass has remaining uses

"Invalid signature"

  • ✅ Client must sign headers with correct private key
  • ✅ Signature must match AccessPass owner
  • ✅ Timestamp must be recent

"ResourceEntry ID not found"

  • ✅ Server should automatically provide X-Resource-Entry-ID header
  • ✅ Check middleware is correctly fetching ResourceEntry
  • ✅ Verify resource is registered in registry

📚 Documentation


🔒 Security Features

  • ✅ On-chain verification (no database required)
  • ✅ Signature verification (prevents forgery)
  • ✅ Zero-knowledge encryption (Seal servers never see content)
  • ✅ Threshold cryptography (multiple key servers required)
  • ✅ Replay protection (nonces prevent request replay)
  • ✅ Time-limited access (SessionKey expires after TTL)

📄 License

[Your License Here]