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

@l8zk/sdk

v1.3.1

Published

Privacy-preserving verifiable credentials using zero-knowledge proofs. Production implementation of the OpenAC protocol.

Readme

@l8zk/sdk

Production-ready SDK for privacy-preserving verifiable credentials using zero-knowledge proofs.

Built on the OpenAC protocol by Privacy and Scaling Explorations (PSE), Ethereum Foundation.

Overview

The L8ZK SDK enables privacy-preserving credential verification where users can prove properties about their credentials (e.g., "I am over 18") without revealing the underlying data (e.g., exact birthdate). Each proof is cryptographically unlinkable, preventing tracking across verifiers.

Key Features

  • Zero-Knowledge Proofs: Generates Spartan2 ZK proofs (~112KB prepare, ~41KB show)
  • No Trusted Setup: Uses transparent Spartan protocol with Hyrax commitments
  • SD-JWT Compatible: Works with Selective Disclosure JWT credentials
  • Cross-Platform: Node.js support (macOS/Linux, x64/ARM64)
  • Fast Performance: ~14s total (one-time setup) + ~100ms per presentation
  • Unlinkable: Each proof is cryptographically reblinded to prevent correlation
  • Auto-Download: Circom artifacts downloaded automatically on first use (~33MB)

Current Status

This SDK generates and verifies real cryptographic ZK proofs using the Spartan2 proving system. The proofs are:

  • Cryptographically sound
  • Properly reblinded for unlinkability
  • Verified by the native Rust backend

Current Limitation: The SDK currently uses pre-compiled circuit inputs for proof generation. Custom credential data support requires circuit recompilation and is under active development. The API and proof flow are production-ready.

Security Properties

  • Zero-Knowledge: Only policy satisfaction is revealed, not the actual credential data
  • Unlinkability: Each presentation is cryptographically unique and cannot be correlated
  • Soundness: Verifier is convinced only if the statement is true
  • Device Binding: Credentials can be bound to a specific device to prevent transfer

Installation

npm install @l8zk/sdk

That's it! The SDK automatically:

  • Installs the correct native binary for your platform (macOS/Linux, x64/ARM64)
  • Downloads circom artifacts (~33MB) on first use

Requirements

  • Node.js 18+

Platform Support

| Platform | Architecture | Package | | -------- | ------------------------ | ------------------------ | | macOS | Apple Silicon (M1/M2/M3) | @l8zk/sdk-darwin-arm64 | | macOS | Intel | @l8zk/sdk-darwin-x64 | | Linux | x64 | @l8zk/sdk-linux-x64 | | Linux | ARM64 | @l8zk/sdk-linux-arm64 |

Platform binaries are installed automatically via npm's optional dependencies.

Quick Start

Basic Usage (Node.js)

import { OpenAC, generateKeyPair, base64UrlEncode } from "@l8zk/sdk";

// Helper: Create a test SD-JWT credential
function createTestCredential(): string {
  const keys = generateKeyPair();
  const now = Math.floor(Date.now() / 1000);

  const header = { alg: "ES256", typ: "vc+sd-jwt" };
  const payload = {
    iss: "https://test-issuer.example.com",
    sub: "did:example:user123",
    iat: now,
    exp: now + 365 * 24 * 60 * 60,
    cnf: { jwk: keys.publicKey },
    _sd: [],
  };

  const headerB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)));
  const payloadB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)));
  const signatureB64 = base64UrlEncode(new Uint8Array(64).fill(1));

  const nameDisclosure = base64UrlEncode(
    new TextEncoder().encode(JSON.stringify(["salt1", "name", "Alice"]))
  );
  const ageDisclosure = base64UrlEncode(
    new TextEncoder().encode(JSON.stringify(["salt2", "roc_birthday", "19901215"]))
  );

  return `${headerB64}.${payloadB64}.${signatureB64}~${nameDisclosure}~${ageDisclosure}`;
}

async function main() {
  // Step 1: Create/obtain an SD-JWT credential
  const credential = createTestCredential();

  // Step 2: Prepare credential (one-time, ~6s)
  const handle = await OpenAC.prepare({
    credential,
    deviceBinding: true,
  });

  // Step 3: Generate proof (per presentation, ~100ms)
  const nonce = OpenAC.generateNonce();
  const proof = await handle.show({
    policy: { age: { gte: 18 } },
    nonce,
  });

  // Step 4: Verify proof (verifier side, ~34ms)
  const result = await OpenAC.verify(proof, { age: { gte: 18 } }, { expectedNonce: nonce });

  console.log(result.valid); // true
}

main();

Complete Example: Age Verification

import { OpenAC, generateKeyPair, base64UrlEncode } from "@l8zk/sdk";

// Helper: Create an SD-JWT credential (in production, this comes from an issuer)
function createCredential(): string {
  const keys = generateKeyPair();
  const now = Math.floor(Date.now() / 1000);

  const header = { alg: "ES256", typ: "vc+sd-jwt" };
  const payload = {
    iss: "https://federal-republic-of-germany.gov",
    sub: "did:example:alice-schmidt",
    iat: now,
    exp: now + 365 * 24 * 60 * 60,
    cnf: { jwk: keys.publicKey },
    _sd: [],
  };

  const headerB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(header)));
  const payloadB64 = base64UrlEncode(new TextEncoder().encode(JSON.stringify(payload)));
  const signatureB64 = base64UrlEncode(new Uint8Array(64).fill(1));

  // Disclosures contain the actual claims
  const birthdayDisclosure = base64UrlEncode(
    new TextEncoder().encode(JSON.stringify(["salt1", "roc_birthday", "19900515"]))
  );
  const nationalityDisclosure = base64UrlEncode(
    new TextEncoder().encode(JSON.stringify(["salt2", "nationality", "DE"]))
  );

  return `${headerB64}.${payloadB64}.${signatureB64}~${birthdayDisclosure}~${nationalityDisclosure}`;
}

async function main() {
  // User: Prepare credential with ZK proof capability
  const credential = createCredential();
  const wallet = await OpenAC.prepare({
    credential,
    deviceBinding: true,
  });

  // Verifier: Request proof of age >= 18
  const challenge = OpenAC.generateNonce();

  // User: Generate privacy-preserving proof
  const presentation = await wallet.show({
    policy: { age: { gte: 18 } },
    nonce: challenge,
  });

  // Verifier: Verify the proof
  const verification = await OpenAC.verify(
    presentation,
    { age: { gte: 18 } },
    { expectedNonce: challenge }
  );

  if (verification.valid) {
    console.log("Access granted: User is 18+");
    // Verifier knows: User is 18+
    // Verifier does NOT know: Exact age, birthdate, name, nationality
  } else {
    console.log("Access denied");
  }
}

main();

Advanced: Multiple Policies

// Crypto exchange onboarding
const proof = await wallet.show({
  policy: {
    age: { gte: 18 },
    nationality: { nin: ["KP", "IR", "SY"] }, // Not in sanctioned countries
    countryCode: { in: ["US", "CA", "GB", "DE", "FR"] }, // Supported regions
  },
  nonce: exchangeChallenge,
});

// EU residency verification
const euProof = await wallet.show({
  policy: {
    countryCode: {
      in: ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE"],
    },
  },
  nonce: serviceChallenge,
});

API Reference

OpenAC.prepare(options)

Prepares a credential for zero-knowledge presentations. This is a one-time operation per credential (~6s).

Parameters:

{
  credential: string;           // SD-JWT credential
  deviceBinding?: boolean;      // Bind to device (default: false)
  storage?: StorageAdapter;     // Storage backend (default: memory)
}

Returns: Promise<CredentialHandle>

Example:

const handle = await OpenAC.prepare({
  credential: sdJwtString,
  deviceBinding: true,
});

handle.show(options)

Generates a zero-knowledge presentation proof (~100ms).

Parameters:

{
  policy: PolicyPredicates;     // What to prove
  nonce: string;                // Verifier challenge
  disclosures?: string[];       // Optional explicit disclosures
}

Returns: Promise<Presentation>

Example:

const proof = await handle.show({
  policy: { age: { gte: 21 } },
  nonce: verifierNonce,
});

OpenAC.verify(proof, policy?, options?)

Verifies a zero-knowledge presentation proof (~34ms).

Parameters:

proof: Proof | SerializedProof;   // Proof to verify
policy?: Policy;                   // Expected policy (optional)
options?: {
  expectedNonce?: string;          // Challenge nonce to verify
  trustedIssuers?: string[];       // Allowed issuer URLs
}

Returns: Promise<VerificationResult>

Example:

const result = await OpenAC.verify(proof, { age: { gte: 21 } }, { expectedNonce: verifierNonce });

Policy Predicates

The SDK supports rich policy expressions:

Comparison Operators

{
  age: { gte: 18 },        // Greater than or equal
  age: { gt: 21 },         // Greater than
  age: { lte: 65 },        // Less than or equal
  age: { lt: 100 },        // Less than
}

Equality

{
  countryCode: "DE",       // Exact match
  nationality: "US",
}

Set Membership

{
  nationality: { in: ["DE", "FR", "IT"] },      // Must be in set
  countryCode: { nin: ["KP", "IR"] },           // Must NOT be in set
}

Range Queries

{
  income: { gte: 50000, lt: 100000 },  // Between 50k and 100k
  age: { gte: 18, lte: 65 },           // Between 18 and 65
}

Combined Policies

{
  age: { gte: 18 },
  nationality: { in: ["US", "CA", "GB"] },
  income: { gte: 50000 },
  countryCode: { nin: ["KP", "IR", "SY"] },
}

Storage Adapters

Memory Storage (Default)

import { MemoryAdapter } from "@l8zk/sdk";

const handle = await OpenAC.prepare({
  credential,
  storage: new MemoryAdapter(),
});

IndexedDB (Browser)

import { IndexedDBAdapter } from "@l8zk/sdk";

const handle = await OpenAC.prepare({
  credential,
  storage: new IndexedDBAdapter("my-wallet"),
});

Custom Storage

import { StorageAdapter } from "@l8zk/sdk";

class MyStorage implements StorageAdapter {
  async get(key: string): Promise<string | null> {
    // Your implementation
  }

  async set(key: string, value: string): Promise<void> {
    // Your implementation
  }

  async delete(key: string): Promise<void> {
    // Your implementation
  }

  async keys(): Promise<string[]> {
    // Your implementation
  }

  async clear(): Promise<void> {
    // Your implementation
  }
}

How It Works

The SDK uses a two-phase approach for efficient ZK proofs:

  1. First Run: Downloads circom artifacts (~33MB) to ~/.l8zk/circom/
  2. Subsequent Runs: Uses cached artifacts for instant startup

The native Rust binary (ecdsa-spartan2) is included in platform-specific npm packages and installed automatically.

Development Setup (Optional)

If you want to build from source or modify the circuits:

# 1. Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 2. Install Circom
# See: https://docs.circom.io/getting-started/installation/

# 3. Initialize submodules
npm run submodule:init

# 4. Build circuits
cd wallet-unit-poc/circom
yarn && yarn compile:jwt && yarn compile:show

# 5. Build Rust binary
cd ../ecdsa-spartan2
cargo build --release

Performance

Benchmarks on Apple M1 Pro:

| Operation | Time | Description | | -------------- | ------ | ---------------------- | | Prepare Setup | ~6s | One-time circuit setup | | Prepare Prove | ~3.6s | Generate prepare proof | | Prepare Verify | ~1.8s | Verify prepare proof | | Show Prove | ~100ms | Generate presentation | | Show Verify | ~34ms | Verify presentation |

Total end-to-end flow: ~13s (one-time) + ~100ms (per presentation)

Architecture

┌─────────────────────────────────────────────────────────────┐
│                        L8ZK SDK                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │   Prepare    │  │     Show     │  │    Verify    │    │
│  │   (Setup)    │  │  (Present)   │  │   (Check)    │    │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘    │
│         │                  │                  │             │
│         └──────────────────┴──────────────────┘             │
│                            │                                │
│                   ┌────────▼────────┐                       │
│                   │  Native Backend │                       │
│                   │  (Spartan2 ZK)  │                       │
│                   └─────────────────┘                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Components

  • Credential Parser: SD-JWT parsing and validation
  • Prover: ZK proof generation (prepare/show phases)
  • Verifier: Proof verification and policy checking
  • Storage: Cross-platform credential storage
  • Native Backend: Rust-based Spartan2 + ECDSA proving system

Examples

See the examples/ directory for complete working examples:

  • examples/age-verification/ - Basic age verification
  • examples/crypto-onboarding/ - Crypto exchange KYC
  • examples/eu-residency/ - EU residency verification
  • examples/full-demo/ - Complete end-to-end demo

Run examples:

npx tsx examples/full-demo/index.ts

Security Considerations

Privacy Guarantees

  • Zero-Knowledge: Only the policy satisfaction is revealed
  • Unlinkability: Each proof is cryptographically reblinded
  • No Correlation: Verifiers cannot link presentations from the same user

What Verifiers Learn

When proving age >= 18:

  • ✅ Verifier knows: User satisfies the policy
  • ❌ Verifier does NOT know: Exact age, birthdate, name, or other attributes

Threat Model

  • Honest Verifier: Verifier follows protocol but may try to learn extra information
  • Malicious Prover: Cannot create valid proofs for false statements
  • Collusion: Multiple verifiers cannot link presentations

Best Practices

  1. Always use nonces: Prevents replay attacks
  2. Verify issuer: Check trustedIssuers list
  3. Rotate credentials: Periodically refresh credentials
  4. Secure storage: Use encrypted storage for sensitive data
  5. Device binding: Enable for high-security use cases

Browser Support

The SDK works in modern browsers with WebAssembly support:

import { OpenAC } from "@l8zk/sdk";
import { IndexedDBAdapter } from "@l8zk/sdk";

// Browser usage
const handle = await OpenAC.prepare({
  credential: sdJwtString,
  storage: new IndexedDBAdapter("wallet"),
});

const proof = await handle.show({
  policy: { age: { gte: 18 } },
  nonce: challenge,
});

React Native Support

import { OpenAC } from "@l8zk/sdk";
import AsyncStorage from "@react-native-async-storage/async-storage";

// Custom storage adapter for React Native
class RNStorage implements StorageAdapter {
  async get(key: string) {
    return await AsyncStorage.getItem(key);
  }
  async set(key: string, value: string) {
    await AsyncStorage.setItem(key, value);
  }
  // ... implement other methods
}

const handle = await OpenAC.prepare({
  credential,
  storage: new RNStorage(),
});

Troubleshooting

Native Backend Not Found

If you see "Native backend not available":

  1. Ensure you're on a supported platform (macOS or Linux, x64 or ARM64)
  2. Try reinstalling: rm -rf node_modules && npm install
  3. Check that the platform package was installed: ls node_modules/@l8zk/

Circom Artifacts Download Failed

If artifact download fails:

  1. Check internet connection
  2. Clear cache and retry: rm -rf ~/.l8zk/circom
  3. The SDK will re-download on next run

Permission Denied (EACCES)

If you see binary permission errors:

  1. The postinstall script should fix this automatically
  2. Manual fix: chmod +x node_modules/@l8zk/sdk-*/bin/ecdsa-spartan2

Performance Issues

For slow proof generation:

  1. First run downloads ~33MB of artifacts - subsequent runs are faster
  2. Proof generation is CPU-intensive (~6s for prepare, ~100ms for show)
  3. Consider caching prepared credentials

Contributing

We welcome contributions! See CONTRIBUTING.md for guidelines.

Development Setup

git clone https://github.com/rahulbarmann/l8zk
cd sdk
npm install
npm run submodule:init
npm test

Running Tests

npm test              # Run all tests
npm run test:watch    # Watch mode
npm run test:coverage # With coverage

Credits

This SDK is built on the OpenAC protocol developed by the zkID team at Privacy and Scaling Explorations (PSE), Ethereum Foundation.

Core Technology

  • Spartan: Transparent SNARK protocol (no trusted setup)
  • Hyrax: Polynomial commitment scheme
  • ECDSA: Signature verification in zero-knowledge
  • SD-JWT: Selective Disclosure JSON Web Tokens

License

MIT License - see LICENSE for details.

Built on OpenAC by PSE / Ethereum Foundation - https://github.com/privacy-scaling-explorations/zkID

Support

  • GitHub Issues: https://github.com/rahulbarmann/l8zk/issues
  • Documentation: https://github.com/rahulbarmann/l8zk
  • Examples: https://github.com/rahulbarmann/l8zk/tree/main/examples

Changelog

See CHANGELOG.md for version history and release notes.