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

@omnid/zkfetch

v0.4.0

Published

fetch with a proof: notarized HTTPS with selective disclosure and zero-knowledge predicates, on TLSNotary.

Readme

zkfetch makes an HTTPS request together with a notary and returns a normal Response plus a signed attestation. Afterwards the user chooses what to reveal (URL, headers, JSON fields) or proves facts about hidden values, such as longestStreak >= 365. The result is a presentation anyone can verify offline. The notary never sees private data such as auth tokens, and the user cannot forge what the server sent.

It is built on TLSNotary (pinned v0.1.0-alpha.15, vendored and patched), with a Rust core, a Bun/Node SDK and a notary that runs on a small ARM server (AWS Graviton, Mumbai).

On top of TLSNotary

| Addition | What it does | | --- | --- | | TLS 1.3 (MPC) | MPC-TLS over TLS 1.3 (AES-128-GCM, P-256): an HKDF key schedule in MPC, public handshake keys, private application keys, authenticated record suffixes. | | Proxy mode, TLS 1.2 and 1.3 | The notary relays the connection and the prover proves the session keys in zero knowledge. Kilobytes of traffic instead of ~66 MB of MPC preprocessing. For TLS 1.3 the notary decrypts and checks the handshake it relayed. | | QuickSilver predicates | Numeric (gte, gt) and JSON-shape proofs over hidden plaintext, checked by the notary during the session and signed into the attestation. The default backend. | | Binius64 predicates | Opt-in. Per-leaf BLAKE3 commitments let the user prove new predicates later, offline, without the notary. | | JSON selective disclosure | Reveal by dotted path (users.0.username), including array elements. With the default QuickSilver backend the notary attests the shape of every JSON value, so verify() returns each disclosed value with a proven path (result.json). Disclosing a field therefore also reveals the response's keys and punctuation, not its other values. | | Session binding | zkf.owner / zkf.context attestation extensions for wallet binding and verifier challenges. | | fetch-shaped SDK | zkFetch(url, init) returns a standard Response. Sessions serialize, so the reveal can be decided later. | | Automatic version and backend | tlsVersion: "auto" prefers TLS 1.3 and falls back to 1.2 only for idempotent requests. One backend switch configures commitments and proofs. | | Single-threaded MPC | A patched executor runs MPC without OS threads, for wasm hosts such as Workers. | | Multi-threaded wasm | A threaded browser build spreads OT work across Web Workers in cross-origin isolated contexts. | | Prepared sessions | prepare() runs the notary connection and preprocessing before the request, so a click only waits for TLS, proofs and attestation. | | Notary hardening | The notary signs the session mode and, in proxy mode, the host it dialed (verifiers reject a different server name); dials only public addresses; caps relayed bytes, predicate work, sessions per client (429) and total sessions (503); and closes connections that do not start a session within 15 s. | | Hosted notary | The native notary on a t4g.small in ap-south-1 behind Caddy (HTTPS), with scripts that create and delete it (infra/aws). |

The protocol patches are listed in vendor/tlsn/ZKFETCH_PATCHES.md and vendor/mpz/ZKFETCH_PATCHES.md. The TLS 1.3 and proxy-mode patches have not had a security review yet.

Performance

All optimizations keep every byte on the wire identical, so old and new provers and notaries interoperate.

| Change | Where | Measured | | --- | --- | --- | | ARM64 PMULL carry-less multiplication, runtime-detected | native ARM64 | proxy session −47% (M2 Max) | | Eight-row Ferret LPN encoder with local accumulators | all | encoder −41–44%, proxy session −5% | | QuickSilver check with lazy GF(2^128) reduction (from emp-toolkit) | all | proxy session −2.8% | | wasm SIMD128 | browsers | prover CPU −40% (with the two above) | | Multi-threaded wasm, 8 threads | isolated browser workers | prover CPU 3.3 s → 1.1 s | | prepare() ahead of the request | browsers | wait after click 6–9 s → 2.3–3 s (live, proxy) | | Notary in Mumbai instead of Cloudflare (reached from India via Hong Kong/Singapore) | hosting | native Duolingo proof, proxy: 15–17 s → 2.1 s (live, from India) |

Live latency is dominated by about 21 sequential prover-notary round trips. Measurements and methods: profiling, first PMULL report.

Request lifecycle

sequenceDiagram
    autonumber
    participant App as App (zkFetch)
    participant Prover as Prover (Rust, native addon)
    participant Notary as Notary (Rust, behind Caddy)
    participant Server as HTTPS server

    App->>Prover: zkFetch(url, { zkConfig })
    Prover->>Notary: wss:// /notarize
    Prover->>Notary: commit: mode, TLS version, limits

    alt MPC mode
        Note over Prover,Notary: MPC preprocessing (OT, garbled circuits)
        Prover->>Server: TLS connection (the prover dials)
        Note over Prover,Notary: Prover and notary jointly run the TLS client:<br/>keys are secret-shared, plaintext stays with the prover
    else Proxy mode
        Notary->>Server: TCP to the server name, port 443 (the notary dials)
        Prover->>Server: TLS through the notary's relay
        Note over Prover,Notary: Notary records the ciphertext. The prover proves<br/>the key schedule in ZK. For TLS 1.3 the notary checks<br/>the handshake records and Finished messages itself
    end

    Prover->>Notary: hash commitments, QuickSilver predicate proofs
    Notary-->>Prover: signed attestation (secp256k1)
    Prover-->>App: Response + res.zk (attestation and secrets)
    App->>App: res.zk.present({ reveal, prove })
    Note over App: Presentation goes to any verifier, which checks it offline:<br/>notary key, server certificate, commitments, predicates

SDK

npm install @omnid/zkfetch
import { zkFetch, verify } from "@omnid/zkfetch";

const res = await zkFetch("https://api.example.com/me", {
  headers: { Authorization: `Bearer ${token}` }, // never revealed
  zkConfig: {
    notaryUrl: "wss://15-207-105-149.sslip.io/notarize", // infra/aws/deployment.json
    mode: "mpc",           // or "proxy": much less traffic, trusts the notary-to-server path
    tlsVersion: "auto",    // "1.3" | "1.2" | "auto"
    backend: "quicksilver",// or "binius" for offline predicates later
    predicates: [{ jsonPath: "streak.length", predicate: { gte: "365" } }],
    context: challenge,    // verifier nonce bound into the attestation
  },
});

await res.json();          // a normal Response

// Reveal one field, prove the hidden streak is at least 365 days.
const presentation = res.zk.present({
  response: { jsonPaths: ["username"] },
  prove: [{ jsonPath: "streak.length", predicate: { gte: "365" } }],
});

// Anyone, offline.
const result = verify(presentation, {
  trustedNotaryKeys: [NOTARY_PUBLIC_KEY],
  expectedContext: challenge,
});
console.log(result.serverName, result.json); // [{ path: "username", value: "…" }]

| API | Purpose | | --- | --- | | zkFetch(url, init) | A notarized fetch. Returns Response & { zk: ZkSession }. | | res.zk.present(spec) | Builds a presentation that discloses only what spec selects and proves spec.prove. | | res.zk.timings | Per-phase latency: connect, setup, TLS, proving, attestation. prewarmed marks connect and setup done ahead. | | prepare(url, zkConfig) | Runs the notary connection and MPC preprocessing (most of the latency) before the request. Pass the result as zkConfig.prepared. Single use, expires after 80 s, falls back to a fresh session. wasm builds; a no-op on the native prover. | | res.zk.toJSON() / restoreResponse(data) | Save a session and present it later. The JSON holds secrets; treat it like a credential. | | verify(presentation, opts) | Offline verification against pinned notary keys (required; allowUntrustedNotary only to inspect), the expected context and the required predicates. Returns the notary-signed mode; a proxy proof must name the server the notary dialed, and rejectProxy accepts only MPC-TLS. recvAuthed/sentAuthed give the authenticated byte ranges: decide from those, since a hidden byte and a literal X look the same in recv. | | startNotary() / startFixture() | A local notary and HTTPS fixture for development and tests. |

Runtimes

import { zkFetch } from "@omnid/zkfetch" resolves to the right prover through conditional exports:

| Runtime | Prover | Notes | | --- | --- | --- | | Node.js ≥ 22 / Bun (servers) | Native Rust addon | Multi-threaded and fastest. Both modes. Prebuilt for macOS arm64 for now; other platforms fall back to the wasm prover automatically (runtime reports which). | | Browsers, web workers | wasm (browser condition) | SIMD, single-threaded by default; no SharedArrayBuffer or cross-origin isolation needed. Run it in a worker to keep the page responsive. Call await init() before present()/verify(). | | Cross-origin isolated workers | multi-threaded wasm | init(wasm, { threads: { module: url-of("@omnid/zkfetch/wasm-threads/zkf.js") } }) from a Web Worker. Falls back to one thread without isolation. About 3× less prover CPU with 8 threads. | | Chrome extensions (MV3) | wasm, in a page worker or the service worker | Needs "wasm-unsafe-eval" in the extension CSP; init(chrome.runtime.getURL("zkf_bg.wasm")). Threads need cross_origin_embedder_policy/cross_origin_opener_policy in the manifest and a worker started from an extension page. See packages/chrome-extension. |

To hide setup latency, prepare while the user is still deciding, then fetch:

const zkConfig = { notaryUrl: NOTARY_URL, mode: "proxy" } as const;
const prepared = prepare("https://api.example.com/me", zkConfig); // e.g. when a dialog opens
// ...later, on click:
const res = await zkFetch("https://api.example.com/me", { headers, zkConfig: { ...zkConfig, prepared } });

Browsers cannot open TCP sockets. Proxy mode works everywhere because the notary dials the server. MPC mode in a browser needs zkConfig.relayUrl, a WebSocket-to-TCP relay. React Native gets its own package later.

Quickstart

Requires Bun and Rust 1.98.1 (selected by rust-toolchain.toml).

bun install
bun run build            # release binaries + native addon
bun run test             # Bun + Rust end-to-end tests
bun run playground       # Duolingo longest-streak demo (see .env.example)

infra/aws/up.sh          # hosted notary on EC2 (infra/aws/down.sh deletes it)

Browser builds need wasm-pack; the threaded build also needs a nightly toolchain with rust-src (ZKF_NIGHTLY picks one, default nightly):

bun run --cwd packages/wasm build           # SIMD, single-threaded
bun run --cwd packages/wasm build:threads   # multi-threaded (optional)
bun run --cwd packages/chrome-extension build   # Duolingo side-panel extension
bun packages/sdk/scripts/build-npm.ts       # the @omnid/zkfetch package

The Chrome extension proves a Duolingo username and longest streak in proxy mode, with threads and a prepared session.