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

@furlpay/x402-guard

v0.2.0

Published

Facilitator-layer hardening for x402 agentic payments — closes all five flaw classes from arXiv:2605.30998: cross-resource substitution, duplicate-settlement race, allowance overdraft, denial of settlement, hidden-compute pricing

Readme

x402-guard

Vendor-neutral facilitator-layer hardening for the x402 agentic payment protocol. Drop it into any x402 facilitator or merchant to close all five implementation flaw classes from the security literature — with a test suite that reproduces each attack and proves it's blocked.

npm

Zero dependencies. TypeScript. Works on Base, Solana, or any x402 deployment — independent of which facilitator or SDK you use. Maintained by FurlPay.

Why

x402 decouples off-chain verification from on-chain settlement to get throughput. That gap is where the money leaks. Free-Riding the Agentic Web: A Systematic Security Analysis of x402 Payments (Ling et al., arXiv:2605.30998) tested official SDKs and a production deployment and found resource-leakage ratios up to 100%. This library implements the paper's defenses for all five flaw classes:

| Flaw | Invariant | What goes wrong | This library | | --- | --- | --- | --- | | F1 Cross-resource substitution | I3 Context Binding | A signature minted for resource A unlocks an equal-priced resource B, because verify() checks value + payee but not the resource ("Pattern 3: blind trust in facilitator"). Found near-universal: 38% of hosts expose same-price sibling clusters. | requestBindingHash / verifyRequestBinding — commit H(method \| uri \| body) and reject mismatches (paper §5.1) | | F2 Duplicate-settlement race | I4 Authorization Uniqueness | Concurrent requests with one nonce all clear verification before any settles on-chain; each gets the full service, one pays. Rolling a timed-out nonce back to unused reopens it for replay. | MemoryNonceStore + guardedSettle — atomic Null→PENDING→SETTLED, never freed on an unknown outcome (paper §5.2, §5.4) | | F3 Allowance overdraft (upto scheme) | I5 Balance Sufficiency | N concurrent requests against one signed allowance each read the full remaining balance before any deducts — total spend N × Vmax against a balance of Vmax. | MemoryAllowanceStore + guardedCharge — pessimistic reserve-commit: escrow Vmax atomically before execution, bill actual, refund the rest (paper §5.3) | | F4 Denial of settlement | I2 Settlement Guarantee | An attacker floods the facilitator with slow settlements; the server has already delivered when honest settlements time out — service rendered, never paid. | SettlementCapacityLimiter in guardedSettle — reserve settlement capacity before serving; overflow gets 429, not free service (paper §5.4) | | F5 Hidden-compute pricing | G2 dynamic billing | Pay-per-token cost is unknowable at quote time; adversarial prompts maximize compute per fixed quote and free-ride the difference. | AdaptivePricer — EWMA-learned billing weight; quote estimate × ratio × margin as the Vmax you escrow, refund honest callers at commit (paper §5.5) |

Install

npm install x402-guard

Use

Wrap your settler with guardedSettle. It runs the F1 binding gate first (no wasted compute on a bad request), then the F2 atomic nonce claim, then your settler — releasing the nonce only when the chain proves nothing landed.

import { guardedSettle, MemoryNonceStore } from "x402-guard";

const store = new MemoryNonceStore(); // swap for a Redis-backed store at scale

const result = await guardedSettle(facilitatorReq, {
  nonce: auth.nonce,
  store,
  // F1: bind the payment to the exact request being served
  binding: { presented: payload.extra?.binding, method: req.method, uri: req.url, body: rawBody },
  // your on-chain settler returns a discriminated outcome
  settler: async (r) => {
    try {
      const tx = await submitOnChain(r);         // confirmed
      return { status: "confirmed", transaction: tx };
    } catch (e) {
      return isProvablyNotLanded(e)
        ? { status: "failed_no_tx" }             // safe to release + retry
        : { status: "unknown" };                 // timeout → keep locked, NEVER replayable
    }
  },
});

if (result.success) deliver(result.transaction);
else refuse(result.reason); // fail closed

The unknown outcome is the whole point

A naïve facilitator deletes the nonce when settlement "fails" — but a facilitator timeout is not proof the transaction failed; it may still confirm. Freeing the nonce lets an attacker replay it. guardedSettle distinguishes:

  • confirmed → nonce is SETTLED (terminal, never replayable)
  • failed_no_tx → chain proved nothing landed → nonce released for an honest retry
  • unknown → nonce stays PENDING forever unless you reconcile it against the chain

F3: charge an upto allowance without overdraft

guardedCharge escrows Vmax atomically before your handler runs, bills what the handler reports, and refunds the difference. Concurrent requests can never spend more than the allowance holds.

import { MemoryAllowanceStore, guardedCharge } from "x402-guard";

const allowances = new MemoryAllowanceStore();
allowances.create(auth.allowanceId, auth.totalAuthorized); // on receipt of the signed upto authorization

const res = await guardedCharge({
  allowanceId: auth.allowanceId,
  vmax: quotedMax,
  store: allowances,
  execute: async () => {
    const output = await runInference(prompt);
    return { actual: meter(output), value: output }; // billed amount is clamped to vmax
  },
});

if (res.success) deliver(res.value); // res.charged ≤ vmax; remainder refunded
else refuse(res.reason);             // "allowance_exhausted" | "execution_failed"

F4: fail closed under settlement pressure

Pass a SettlementCapacityLimiter to guardedSettle and capacity is reserved before the nonce is claimed or your settler runs. When the settlement path is saturated (or under attack), overflow requests get settlement_capacity_exhausted — map it to HTTP 429. The refused authorization is untouched, so an honest client simply retries.

import { SettlementCapacityLimiter } from "x402-guard";

const capacity = new SettlementCapacityLimiter(32); // concurrent settlements you can actually clear

const result = await guardedSettle(req, { nonce, store, capacity, settler });
if (!result.success && result.reason === "settlement_capacity_exhausted") {
  return res.status(429).set("Retry-After", "1").end(); // never serve for free
}

F5: price compute you can't predict

For pay-per-token inference the true cost is unknown at quote time — no static price is safe. AdaptivePricer learns the observed actual/estimated ratio (EWMA, clamped) and quotes a Vmax with enough headroom to cover it. Compose it with the F3 escrow: free-riders stop leaking once the weight adapts, and honest callers get the overshoot refunded at commit.

import { AdaptivePricer } from "x402-guard";

const pricer = new AdaptivePricer({ unitPrice: 10n }); // atomic units per output token

const vmax = pricer.quoteMax(estimatedTokens);          // demand this in the 402 challenge
// ... guardedCharge({ vmax, execute }) as above, billing pricer.bill(actualTokens)
pricer.observe(estimatedTokens, actualTokens);          // feed every settlement back

Redis at scale

NonceStore is a 4-method interface. The in-memory default is atomic within one process; for a multi-instance facilitator back acquire with SET key PENDING NX (or Postgres INSERT … ON CONFLICT DO NOTHING) — the atomic check-and-set maps directly.

class RedisNonceStore implements NonceStore {
  async acquire(nonce: string) {
    return (await redis.set(`x402:${nonce}`, "pending", "NX")) === "OK";
  }
  async markSettled(nonce: string) { await redis.set(`x402:${nonce}`, "settled"); }
  async release(nonce: string) { /* only if still pending */ await redis.del(`x402:${nonce}`); }
  async state(nonce: string) { return (await redis.get(`x402:${nonce}`)) as any; }
}

Test suite

npm test reproduces every attack and asserts it fails: the F2 20-concurrent-request race (the paper's Base reproduction — exactly one settles, the settler runs exactly once), the F3 overdraft (20 concurrent charges against one allowance — exactly ⌊balance/Vmax⌋ served, balance never negative), the F4 flood (overflow refused before delivery, authorizations left intact for retry), and F5 convergence (a 5× compute free-rider is fully covered after adaptation, with the learned ratio clamped at both ends).

Scope

This library hardens the state machine around your settler — it does not verify signatures or submit transactions. Wiring these primitives into a live facilitator (Redis-backed stores, the /verify//settle endpoints) is deployment work; the FurlPay integration is tracked in furlpay-x402#3.

Security

Report vulnerabilities to [email protected] — please don't open public issues.

License

MIT