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

@sesamy/capsule

v0.1.0

Published

Client-side decryption library for Capsule - Secure article unlocking using Web Crypto API

Readme

Capsule Client Library

Browser-side decryption library for the Capsule secure article-locking system.

Features

  • Secure Key Generation: RSA-OAEP (2048 or 4096-bit) key pairs generated in the browser
  • Non-extractable Private Keys: Private keys stored with extractable: false in IndexedDB
  • Web Crypto API: Uses native browser cryptography for maximum security
  • Promise-based IndexedDB: Uses the idb library for easy key persistence
  • Multi-key Support: Manage multiple keys with different identifiers (tier-based, article-specific, etc.)

Installation

npm install @sesamy/capsule

Usage

Initialize the Client

import { CapsuleClient } from "capsule-client";

// Default client (single key)
const client = new CapsuleClient();

// Multi-key scenario (e.g., tier-based or article-specific keys)
const premiumClient = new CapsuleClient({ keyId: "premium-tier" });
const articleClient = new CapsuleClient({ keyId: "article-123" });

Generate and Store Keys (First Time Setup)

// Generate a new RSA key pair and store in IndexedDB
const publicKeyB64 = await client.generateKeyPair();

// Send publicKeyB64 to your server for registration
await fetch("/api/register-key", {
  method: "POST",
  body: JSON.stringify({ publicKey: publicKeyB64 }),
  headers: { "Content-Type": "application/json" },
});

Check for Existing Keys

const hasKeys = await client.hasKeyPair();

if (!hasKeys) {
  // Need to generate keys
  const publicKey = await client.generateKeyPair();
  // ... register with server
}

Export Public Key (if needed later)

const publicKeyB64 = await client.getPublicKey();

Decrypt an Article

// Fetch encrypted payload from server
const response = await fetch("/api/article/123");
const encryptedPayload = await response.json();

// Decrypt using the stored private key
const articleContent = await client.decryptArticle(encryptedPayload);

// Display the decrypted content
document.getElementById("article").textContent = articleContent;

Full Example

import { CapsuleClient } from "capsule-client";

const client = new CapsuleClient();

async function setupEncryption() {
  // Check if we already have keys
  const hasKeys = await client.hasKeyPair();

  if (!hasKeys) {
    // Generate new key pair
    const publicKey = await client.generateKeyPair();

    // Register public key with server
    await fetch("/api/user/public-key", {
      method: "POST",
      body: JSON.stringify({ publicKey }),
      headers: { "Content-Type": "application/json" },
    });
  }
}

async function readArticle(articleId: string) {
  // Fetch encrypted article
  const response = await fetch(`/api/articles/${articleId}`);
  const payload = await response.json();

  // Decrypt and display
  try {
    const content = await client.decryptArticle(payload);
    document.getElementById("article-content").innerHTML = content;
  } catch (error) {
    console.error("Failed to decrypt article:", error);
    // Handle decryption failure (e.g., key mismatch)
  }
}

// Initialize on page load
setupEncryption();

API Reference

CapsuleClient

The main client class for key management and decryption.

Constructor

new CapsuleClient(options?: CapsuleClientOptions)

Options:

  • keySize: RSA key size in bits (default: 2048, can be 4096)
  • dbName: IndexedDB database name (default: 'capsule-keys')
  • storeName: IndexedDB store name (default: 'keypair')
  • keyId: Key identifier for multi-key scenarios (default: 'default')

Methods

generateKeyPair(): Promise<string>

Generate a new RSA-OAEP key pair and store in IndexedDB.

Returns the Base64-encoded SPKI public key to send to the server.

hasKeyPair(): Promise<boolean>

Check if a key pair exists in IndexedDB.

getPublicKey(): Promise<string>

Get the Base64-encoded public key (if stored).

decryptArticle(payload: EncryptedPayload): Promise<string>

Decrypt an encrypted article payload.

decryptContent(payload: EncryptedPayload): Promise<ArrayBuffer>

Decrypt content and return raw bytes.

clearKeys(): Promise<void>

Delete all stored keys from IndexedDB.

EncryptedPayload

interface EncryptedPayload {
  encryptedContent: string; // Base64
  iv: string; // Base64
  encryptedDek: string; // Base64
}

Security Notes

  1. Private key never leaves the browser - Stored as non-extractable in IndexedDB
  2. AES key only in memory - DEK is decrypted only for immediate use
  3. Web Crypto API - Uses hardware-accelerated native cryptography
  4. RSA-OAEP with SHA-256 - Secure key wrapping compatible with server libraries

Browser Compatibility

Requires browsers with Web Crypto API support:

  • Chrome 37+
  • Firefox 34+
  • Safari 11+
  • Edge 12+

Development

npm install
npm run build
npm test

License

MIT