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

@prxvt/hook

v0.2.0

Published

Privacy layer for the Agentic Commerce Protocol (ERC-8183) — AES-256-GCM encryption, ECDH key wrapping, ZK proof generation, and on-chain envelope construction for PrivacyHook

Readme

@prxvt/hook

Privacy SDK for ERC-8183 PrivacyHook. Handles AES-256-GCM encryption, ECDH key wrapping, ZK proof generation, IPFS upload, and on-chain envelope construction.

Install

npm install @prxvt/hook ethers

Quick Start

Provider: Encrypt and Submit

import {
  encryptAndPrepareSubmission,
  buildConfigParams,
  PinataAdapter,
} from "@prxvt/hook";

// 1. Configure privacy on-chain (at setBudget time)
const configParams = buildConfigParams(
  "0x0000000000000000000000000000000000000000", // no ZK verifier
  2 // require 2 wrapped keys (client + evaluator)
);
await core.connect(provider).setBudget(jobId, budget, configParams);

// 2. Encrypt and prepare submission
const ipfs = new PinataAdapter({ apiKey: "...", secretApiKey: "..." });
const plaintext = new TextEncoder().encode("Private job output");

const { deliverable, optParams } = await encryptAndPrepareSubmission(
  plaintext,
  [
    { publicKey: clientPubKey, label: "client" },
    { publicKey: evaluatorPubKey, label: "evaluator" },
  ],
  ipfs
);

// 3. Submit on-chain
await core.connect(provider).submit(jobId, deliverable, optParams);

Recipient: Fetch and Decrypt

import { fetchAndDecrypt, PinataAdapter } from "@prxvt/hook";

const ipfs = new PinataAdapter({ apiKey: "...", secretApiKey: "..." });

const plaintext = await fetchAndDecrypt(
  cid,                // IPFS CID from EncryptedSubmission event
  wrappedKey,         // your wrapped key entry from the event
  recipientPrivateKey,
  ipfs
);

Standalone Key Wrapping

import { wrapKey, unwrapKey } from "@prxvt/hook";

// Wrap a data key for a recipient
const wrappedKey = wrapKey(dataKey, recipientPublicKey);

// Unwrap using the recipient's private key
const recoveredKey = unwrapKey(wrappedKey, recipientPrivateKey);

ZK Proof Generation

import { generateGroth16Proof, buildConfigParams } from "@prxvt/hook";

// Configure with ZK verifier and 3 public inputs (for selective disclosure)
const configParams = buildConfigParams(zkVerifierAddress, 2, 3);

// Generate proof
const proof = await generateGroth16Proof(secretPreimage, jobId, {
  wasmPath: "./circuits/build/privacy_preimage_js/privacy_preimage.wasm",
  zkeyPath: "./circuits/build/privacy_preimage_final.zkey",
});

API Reference

Encryption

| Function | Description | |----------|-------------| | encryptForRecipients(plaintext, recipients) | Encrypt data with AES-256-GCM, wrap key per recipient via ECDH | | decryptWithPrivateKey(ciphertext, wrappedKey, privateKey) | Unwrap key and decrypt ciphertext |

Key Wrapping

| Function | Description | |----------|-------------| | wrapKey(dataKey, recipientPubKey, ephemeralPrivKey?) | Wrap a 32-byte key for a single recipient (returns 94-byte v1 key) | | unwrapKey(wrappedKey, recipientPrivateKey) | Unwrap a v1 wrapped key to recover the data key |

Envelope Construction

| Function | Description | |----------|-------------| | buildConfigParams(zkVerifier, minWrappedKeys, numPublicInputs?) | ABI-encode setBudget optParams | | buildEnvelopeParams(cid, wrappedKeys, zkProof?) | ABI-encode submit optParams | | bytesToHex(bytes) | Convert Uint8Array to 0x-prefixed hex string |

CID Utilities

| Function | Description | |----------|-------------| | cidToBytes32(cid) | Convert IPFS CID string to bytes32 (keccak256) | | verifyCidMatch(cid, deliverable) | Check CID maps to expected deliverable hash |

High-Level

| Function | Description | |----------|-------------| | encryptAndPrepareSubmission(plaintext, recipients, ipfs, zkProof?) | Full flow: encrypt → IPFS upload → build submit params | | fetchAndDecrypt(cid, wrappedKey, privateKey, ipfs) | Full flow: IPFS download → decrypt |

ZK Proving

| Function | Description | |----------|-------------| | generateGroth16Proof(preimage, jobId, options) | Generate Groth16 proof for privacy_preimage circuit | | poseidonHash(preimage) | Compute Poseidon hash matching the circuit | | formatProofForChain(snarkjsProof) | ABI-encode proof for on-chain submission |

IPFS Adapters

| Adapter | Description | |---------|-------------| | PinataAdapter({ apiKey, secretApiKey, gateway? }) | Pinata pinning service | | LocalNodeAdapter({ url? }) | Local IPFS node (Kubo HTTP API, default localhost:5001) |

Implement IpfsAdapter interface for custom backends:

interface IpfsAdapter {
  upload(data: Uint8Array): Promise<string>;   // returns CID
  download(cid: string): Promise<Uint8Array>;
}

Constants

| Constant | Value | Description | |----------|-------|-------------| | WRAPPED_KEY_VERSION | 0x01 | Current wrapped key format version | | WRAPPED_KEY_V1_LENGTH | 94 | Byte length of a v1 wrapped key |

Wrapped Key Format (v1)

Each recipient gets a 94-byte wrapped key entry:

Offset  Length  Field
──────  ──────  ────────────────────────────
0       1       Version byte (0x01)
1       33      Ephemeral secp256k1 public key (compressed)
34      12      AES-GCM wrapping nonce
46      48      Encrypted AES key (32) + GCM auth tag (16)
──────  ──────  ────────────────────────────
Total   94 bytes

Key Derivation

  1. Generate ephemeral secp256k1 key pair (one per envelope)
  2. For each recipient's public key:
    • Compute ECDH shared secret
    • Derive 256-bit wrapping key via HKDF-SHA256 (info="erc8183-privacy-wrap")
    • Encrypt AES data key with AES-256-GCM using the wrapping key

Versioning

The version byte enables future format evolution without breaking existing integrations. The on-chain PrivacyHook validates both the length (94 bytes) and version byte (0x01) of each wrapped key.

Crypto Details

| Component | Algorithm | |-----------|-----------| | Data encryption | AES-256-GCM (random 96-bit nonce) | | Key wrapping | ECDH on secp256k1 → HKDF-SHA256 → AES-256-GCM | | Key derivation | HKDF-SHA256 with info = "erc8183-privacy-wrap" | | Ephemeral keys | One secp256k1 key pair per envelope | | ZK proofs | Groth16 on BN128 (snarkjs) |

Ciphertext Format

dataNonce (12) || AES-GCM ciphertext || authTag (16)