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

@zkrune/x402-verify

v0.1.0

Published

Endpoint-level zero-knowledge eligibility gate for x402 services. Verify a zkRune Groth16 proof on-chain before serving a paid response.

Readme

@zkrune/x402-verify

Endpoint-level zero-knowledge eligibility gate for x402 services.

x402 answers who paid. zkRune answers who is allowed. This package verifies a zkRune Groth16 proof against the on-chain verifier before your endpoint serves a paid response — server-enforced, around 50 ms, no gas, no wallet, no personal data.

It reads HTTP headers only. It never inspects or mutates the request body, so it composes with any x402 handler regardless of what the endpoint does.


Where it sits

A request to a regulated endpoint has three independent questions attached. They are orthogonal — each can pass or fail on its own:

| Question | Answered by | Example | |---|---|---| | Is a unique human behind this agent? | Agentlink (Humanode) | freemium anti-Sybil | | Did this request pay? | x402 | $0.06 USDC micro-payment | | Is this caller allowed? | zkRune | 18+, in a permitted jurisdiction, holds a license |

Xona already runs the first two. @zkrune/x402-verify adds the third. The gate runs alongside the x402 payment check — both must pass, order does not matter.


How it works

client                         x402 endpoint
  |  POST /image/flux-2-flex      |
  |  (no zkRune headers)          |
  |------------------------------>|
  |   403 zkrune_eligibility_required
  |<------------------------------|   { circuit, verifier, generateProofAt }
  |                               |
  |  generate proof at zkrune.com (in the browser / agent SDK, ~0.5s)
  |                               |
  |  POST /image/flux-2-flex      |
  |  X-Payment: <x402 ...>        |
  |  X-zkRune-Proof: <base64 ...> |
  |  X-zkRune-Circuit: age-verification
  |------------------------------>|   verifyProofStatic() view call -> Base
  |   200  (image bytes)          |
  |<------------------------------|

The 403 rejection mirrors the x402 402 challenge: it is self-describing, so a client learns exactly which circuit to prove and retries — the same retry loop x402 already uses for X-Payment.


Header contract

A gated request carries three headers alongside the standard X-Payment:

| Header | Required | Value | |---|---|---| | X-zkRune-Proof | yes | base64 of JSON.stringify({ proof, publicSignals }) | | X-zkRune-Circuit | yes | circuit name, e.g. age-verification | | X-zkRune-Verifier | no | base — informational; this package targets Base |

proof is a Groth16 proof in snarkjs shape (pi_a, pi_b, pi_c). publicSignals is the array snarkjs emits. Together they are ~1–2 KB base64 — well within HTTP header limits.


Install

npm install @zkrune/x402-verify viem

viem is a peer dependency — the gate uses it for the read-only Base call.


Usage

Hono

import { Hono } from "hono";
import { zkRuneHonoMiddleware } from "@zkrune/x402-verify";

app.post(
  "/image/flux-2-flex",
  zkRuneHonoMiddleware({ requiredCircuit: "age-verification" }),
  x402(),          // existing payment middleware, unchanged
  fluxHandler,     // existing handler, unchanged
);

Express

import { zkRuneExpressMiddleware } from "@zkrune/x402-verify";

app.post(
  "/image/flux-2-flex",
  zkRuneExpressMiddleware({ requiredCircuit: "age-verification" }),
  x402PaymentMiddleware(),
  fluxHandler,
);

Next.js route handler / Fetch runtimes

import { zkRuneFetchGuard } from "@zkrune/x402-verify";

const guard = zkRuneFetchGuard({ requiredCircuit: "age-verification" });

export async function POST(req: Request) {
  const blocked = await guard(req);
  if (blocked) return blocked;          // 403 challenge or 503
  // ...x402 payment check, then serve
}

See examples/flux-2-flex.ts for the full wiring against Xona's real endpoint.


Public-signal policy

A valid proof only means the math checks out. To pin what was proven, pass validatePublicSignals. For age-verification, snarkjs emits public signals as [isValid, currentYear, minimumAge]:

zkRuneHonoMiddleware({
  requiredCircuit: "age-verification",
  validatePublicSignals: (signals) => {
    const [isValid, currentYear, minimumAge] = signals.map(Number);
    const year = new Date().getUTCFullYear();
    return isValid === 1 &&
           (currentYear === year || currentYear === year - 1) &&
           minimumAge >= 18;
  },
});

Without this check, a client could present a proof generated against a stale year or a minimumAge of zero. The circuit guarantees the relation; the endpoint pins the parameters.


Available circuits

The verifier supports 13 production circuits. The ones most relevant to a creative-AI endpoint:

| Circuit | Proves | |---|---| | age-verification | age ≥ a threshold, without revealing the birth year | | membership-proof | membership in an allow-list (e.g. permitted jurisdictions) | | credential-proof | holding a valid, unexpired credential or license | | balance-proof | a token balance ≥ a threshold, without revealing the balance | | whale-holder | holdings above a tier, without revealing the wallet |

Full list and circuit sources: https://zkrune.com


Hardening — known limitation

The age-verification circuit's public signals are (currentYear, minimumAge) with no nullifier. A captured X-zkRune-Proof header is therefore replayable — anyone who observes it can present the same proof again.

For age gating this is low severity: a replayed proof still attests a true "18+" claim and leaks no personal data. For balance or holdings gating it matters more. Two hardening options for v2, available on request:

  1. Bind the proof to the payment. Include the x402 payment transaction hash as a public input, so a proof is only valid for one paid request.
  2. Nullifier + freshness window. Add a per-proof nullifier and a signed timestamp; the endpoint rejects reused nullifiers and stale proofs.

Both are circuit-level changes on the zkRune side — no change to this package's header contract.


Verifier

| Chain | Contract | Call | |---|---|---| | Base mainnet | 0xa03A353d890033aC9b3044776440C2a4c9E849EA | verifyProofStatic (view) |

Solana and Sui verifiers accept the same proof envelope through a different call interface. Contact zkRune to gate against those.


Contact

zkRune — https://zkrune.com · [email protected]

Reference integration: https://zkrune.com/integrations/xona