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

charproof

v1.0.8

Published

Standalone zero-knowledge encryption, device management, and secure identity library built for Cloud Firestore.

Readme

Charproof

npm version

Charproof is a standalone zero-knowledge encryption and identity management library built for Cloud Firestore. It is designed to secure application data on the client side before saving to the database, supporting multiple authorized devices, decentralized key sharing, and secure recovery keys via WebAuthn PRF.

Features

  • Client-Side Zero-Knowledge Encryption: All application ledger events, user credentials, and metadata are encrypted client-side using WebCrypto.
  • Multi-Device Support: Securely enroll and authorize secondary devices without exposing decryption keys to the server.
  • Hardware-Backed Recovery: Securely backup and recover access keys using WebAuthn's PRF extension.
  • Built for Firestore: Integrates directly with Cloud Firestore stores.

Installation

Install from the NPM registry:

npm install charproof

Scripts

Build

Compile the TypeScript library:

npm run build

Test

Run the full Vitest suite (includes unit, integration, and fuzz/concurrency checks):

npm test

Security

Charproof's zero-knowledge guarantees depend on how you deploy it. Before going to production, read SECURITY.md and deploy the bundled firestore.rules. In short, you must:

  • Deploy the Firestore security rules — they enforce per-user isolation and append-only, immutable event logs. The guarantees do not hold without them.
  • Set an authorized-signer allowlist for multi-writer ledgers (session.setAuthorizedSigners(...)), otherwise any holder of the shared key can impersonate another author.
  • Verify devices out-of-band at enrollment using the 6-digit code (getLocalVerificationCode() / approveDeviceAuthorization(d, { expectedVerificationCode })).
  • Understand that device revocation is forward-only — it cannot claw back data a revoked device already decrypted.

There is no runtime mock/plaintext-crypto switch; the production crypto provider is WebCrypto unless you explicitly inject one via initializeZK({ db, auth, cryptoProvider }).

Developer & API Guide

Charproof provides a comprehensive API for bootstrapping a zero-knowledge context, managing secondary devices, encrypting transaction logs, and setting up hardware/phrase-based recovery.

1. Bootstrapping and Dependency Injection

Before invoking any zero-knowledge operations, configure the database and authentication providers using your application's Firebase instance:

import { initializeZK } from 'charproof';
import { getFirestore } from 'firebase/firestore';
import { getAuth } from 'firebase/auth';

// Initialize the zero-knowledge environment
initializeZK({
  db: getFirestore(),
  auth: getAuth()
});

2. Device Lifecycle and Key Management

Every device enrolling in a user's ZK workspace maintains its own local key pairs (RSA-OAEP for key wrapping, ECDSA for signing) saved in IndexedDB.

Requesting Device Enrollment (New Device)

On a new, unauthorized device, trigger the authorization request:

import { requestDeviceAuthorization, getActiveAmk } from 'charproof';

try {
  // Generates local keys and registers request in the Account Keystore
  await requestDeviceAuthorization();
  console.log("Device authorization requested. Waiting for approval...");
} catch (error) {
  console.error("Failed to request enrollment:", error);
}

Approving a Device (Primary Device)

On an already authorized device, listen for and approve pending devices:

import { subscribePendingRequests, approveDeviceAuthorization } from 'charproof';

// Listen to pending requests
subscribePendingRequests((pendingDevices) => {
  pendingDevices.forEach(async (device) => {
    // Approve device (re-seals the Account Master Key using the pending device's public key)
    await approveDeviceAuthorization(device);
    console.log(`Approved device: ${device.deviceName}`);
  });
});

3. ZK Encrypted Ledger Sessions

Charproof lets you record, append, and stream fully client-side encrypted event logs (ledgers) securely.

Creating a New Ledger

import { createLedgerSession } from 'charproof';

// Creates a new random symmetric key, wrapping it using the active AMK
const { session, ledgerId, ownershipToken } = await createLedgerSession();

// Append encrypted events client-side
await session.appendEvent({
  type: "WRITE_DATA",
  payload: { someSensitiveField: "secrets" }
});

Loading an Existing Ledger

import { getLedgerSession } from 'charproof';

// Load the ledger session using the ledgerId and decrypted symmetric key
const session = await getLedgerSession(ledgerId, {
  shareableKey: decryptedSymmetricKey
});

// Subscribe to real-time decrypted updates
const unsubscribe = session.subscribe((events) => {
  events.forEach((event) => {
    console.log("Decrypted event action:", event.action);
    console.log("Verified Signer:", event.signerPublicKey);
  });
});

4. Asymmetric Phrase Recovery (BIP39 Mnemonic Backup)

Enables users to safely backup their Account Master Key using a secure 24-word recovery phrase.

Setting up Recovery Phrase

import { setupPhraseRecovery } from 'charproof';

// Generates 24-word phrase, derives symmetric protector, and wraps the AMK
const phrase = await setupPhraseRecovery();
console.log(`Write down your 24-word recovery phrase: "${phrase}"`);

Recovering the AMK

import { recoverAmkWithPhrase } from 'charproof';

// Recovers the AMK from the recovery phrase on a completely clean device
const { amk, amkId } = await recoverAmkWithPhrase(phrase);
console.log(`AMK successfully recovered! ID: ${amkId}`);

5. Core Interfaces & Types Reference

All types and interfaces are exported at the library root and can be imported from charproof.

LedgerSession

The primary interface for client-side encrypted event log interaction:

export interface LedgerSession {
  // Appends a new event payload (encrypted client-side with AES-GCM and signed with ECDSA)
  appendEvent(action: any): Promise<void>;
  
  // Subscribes to real-time decrypted updates of the log
  subscribe(onUpdate: (events: DecryptedLedgerEvent[]) => void): () => void;
  
  // Retrieves the first event in the ledger (Genesis)
  getGenesisEvent(): Promise<DecryptedLedgerEvent | null>;
  
  // Exports the raw decrypted symmetric key for the ledger
  exportSessionKey(): string;
  
  // Exports the public signing key associated with the local device identity
  getSignerPublicKey(): string;
}

DecryptedLedgerEvent

Emitted by ledger subscribers when a new event is retrieved and decrypted:

export interface DecryptedLedgerEvent {
  action: any;             // The original decrypted payload (e.g. { type: 'VOTE' })
  signerPublicKey: string; // Base64 SPKI public key of the device that authored this event
}

AccountKeysDocument

The schema structure of the root account document stored in Firestore (/users/{uid}):

export interface AccountKeysDocument {
  activeAmkId: string;                      // The active Account Master Key version ID (e.g., "amk_v1")
  devices: Record<string, DevicePublicKey>;  // Enrolled devices mapped by deviceId
  recoveryMethods: Record<string, RecoveryMethod>; // Configured recovery methods mapped by methodId
  keyring: Record<string, Record<string, string>>; // Matrix of amkId -> deviceId -> wrapped_amk_base64
}

PendingDevice

The structure of a secondary device enrollment request pending primary approval:

export interface PendingDevice {
  deviceId: string;
  publicKey: string; // Base64 SPKI RSA Public Key of the new device
  status: 'pending' | 'authorized' | 'rejected';
  createdAt: number;
  encryptedDeviceName: {
    encryptedData: string;
    iv: string;
    wrappedKeys: Record<string, string>; // Maps primary device ID -> encrypted ephemeral symmetric key
  };
}

Example Application

Charproof includes a comprehensive interactive example application and end-to-end integration test runner that demonstrates the complete cryptographic lifecycle:

  1. Stage 1 (Genesis): Setting up the initial Account Master Key (AMK) and writing encrypted ledger events.
  2. Stage 2 (Enrollment): Registering and authorizing a secondary device B to the user's account.
  3. Stage 3 (Decryption): Retrieving active AMK on device B and client-side decrypting the ledger events.
  4. Stage 4 (Phrase Recovery): Generating a mnemonic phrase, and recovering the AMK on a clean, new device C.

To run the example app:

npm run example