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

@telaro/x402

v1.0.0

Published

x402 payment gate for Solana. Verifies the paying agent's Telaro bond before settlement. Adapters for Express, Hono, and Next.js.

Readme

@telaro/x402

Telaro bond/score gate for x402 services on Solana.

x402 answers "did the agent pay." It does not answer "should I trust this agent to transact." @telaro/x402 closes that gap. It pulls the paying wallet out of the x402 payment payload and runs a Telaro bond + score check on it before the payment settles.

pnpm add @telaro/x402

Two ways to call it

The package ships two calling conventions side by side:

  • The v0.x positional signature (gate(policy, options?)). Stable, unchanged in 1.0.0-beta. Use this if you are migrating from 0.x and want the smallest diff.
  • The v1 strategy switch (gate({ strategy, policy, options, keyLinkResolver })). The shape the protocol is converging on. Picks how the payment is interpreted (trust, and eventually sacp, sacp+trust) and exposes the AgentKeyLink hot-key fallback.

Both produce the same X402GateResult. The choice is purely about which surface you want to commit to.

Express

v0.x positional (no change from 0.2):

import { expressX402Gate } from "@telaro/x402";

app.post(
  "/premium",
  expressX402Gate({ minBond: 1_000_000_000n, minScore: 700 }),
  yourX402PaymentMiddleware,
  (_req, res) => res.json({ ok: true }),
);

v1 strategy object (recommended for new integrations):

import { expressX402Gate } from "@telaro/x402";

app.post(
  "/premium",
  expressX402Gate({
    strategy: "trust",
    policy: { minBond: 1_000_000_000n, minScore: 700 },
    keyLinkResolver: async (wallet) => fetchKeyLinkPda(wallet),
  }),
  yourX402PaymentMiddleware,
  (_req, res) => res.json({ ok: true }),
);

Hono

import { honoX402Gate } from "@telaro/x402";

// v0.x
app.use("/premium", honoX402Gate({ minBond: 1_000_000_000n, minScore: 700 }));

// v1
app.use("/premium", honoX402Gate({
  strategy: "trust",
  policy: { minBond: 1_000_000_000n, minScore: 700 },
}));

Next.js / Fetch handlers

import { fetchX402Gate, getX402Result } from "@telaro/x402";

// v0.x
export const POST = fetchX402Gate(
  async (req) => Response.json({ ok: true, payer: getX402Result(req)?.payer }),
  { minBond: 1_000_000_000n, minScore: 700 },
);

// v1
export const POST = fetchX402Gate(
  async (req) => Response.json({ ok: true, payer: getX402Result(req)?.payer }),
  {
    strategy: "trust",
    policy: { minBond: 1_000_000_000n, minScore: 700 },
  },
);

Any framework: evaluateX402 and evaluateX402Payment

Two functions, same semantics. Use whichever style fits.

import { evaluateX402, evaluateX402Payment } from "@telaro/x402";

// v0.x positional
const a = await evaluateX402Payment(
  header,
  { minBond: 1_000_000_000n, minScore: 700 },
);

// v1 strategy object
const b = await evaluateX402(header, {
  strategy: "trust",
  policy: { minBond: 1_000_000_000n, minScore: 700 },
});

extractPayer(serializedTransactionBase64) is also exported if you only need to identify the paying wallet.

Rejection shape

A rejected request gets a JSON body and one of three HTTP statuses:

| Status | When | | --- | --- | | 402 | payment missing or unusable (NO_PAYMENT, MALFORMED_PAYMENT, NO_TRANSFER, UNSIGNED_PAYER) | | 403 | agent identified but fails the policy (NOT_BONDED, BOND_BELOW_MIN, SCORE_BELOW_MIN, FROZEN) | | 502 | the Telaro lookup itself failed (LOOKUP_FAILED) |

Pass onReject to render the rejection in your own shape (Express only).

AgentKeyLink fallback (v1)

Agents that have rotated their hot key through AgentKeyLink sign x402 payments with payment_key, not the cold controller. The default derive-and-lookup misses them. Provide keyLinkResolver to plug in the fallback:

The Telaro web hosts a ready-made endpoint at https://telaro.xyz/api/agent/by-payment-key/<wallet>. It does the getProgramAccounts scan once and caches the result. Drop it in:

import type { KeyLinkResolver } from "@telaro/x402";

const keyLinkResolver: KeyLinkResolver = async (wallet) => {
  const res = await fetch(
    `https://telaro.xyz/api/agent/by-payment-key/${wallet}`,
  );
  if (!res.ok) return null;
  const body = await res.json();
  return body.agent_pda ?? null;
};

Want to scan yourself instead? The layout filter is:

import { Connection, PublicKey } from "@solana/web3.js";
import { decodeAgentKeyLink } from "@telaro/sdk";
import bs58 from "bs58";

const keyLinkResolver: KeyLinkResolver = async (wallet) => {
  const accs = await connection.getProgramAccounts(TELARO_PROGRAM_ID, {
    filters: [
      { dataSize: 152 }, // AgentKeyLink size
      { memcmp: { offset: 80, bytes: bs58.encode(new PublicKey(wallet).toBytes()) } },
    ],
  });
  if (accs.length === 0) return null;
  const link = decodeAgentKeyLink(Buffer.from(accs[0].account.data));
  return link?.agent.toBase58() ?? null;
};

Pass it through the v1 config or as part of the v0.x options:

expressX402Gate({
  strategy: "trust",
  policy: { minBond: 1_000_000_000n, minScore: 700 },
  keyLinkResolver,
});

The resolver is only called on NOT_BONDED. Policy-rejection codes (BOND_BELOW_MIN, SCORE_BELOW_MIN, FROZEN) skip the fallback because the agent was already identified and the policy decided.

How identity works

The agent is identified by the SPL transfer authority that signs the x402 payment transaction. That authority must map to an agent in the Telaro registry. Two valid mappings:

  1. Direct: the wallet's findAgentPda(wallet) derivation hits a registered agent. This is the v0.x default.
  2. Linked: the wallet is a hot payment_key registered against an agent through AgentKeyLink. Requires keyLinkResolver.

The authority is read from the transfer instruction, not the fee payer. That means the gate works whether the agent pays its own gas or a fee-free facilitator does. A delegated transfer signs with a key the gate has no record of and fails as NOT_BONDED.

sACP strategy (subpath: @telaro/x402/sacp)

The sACP strategy turns a funded sACP Job into the payment proof for an HTTP request. Different shape from the trust strategy: a separate header (x-sacp-job-id), on-chain verification, real escrow.

Add the peer deps when you need this path:

pnpm add @telaro/x402 @telaro/sacp express

Server

import { withSacpX402 } from "@telaro/x402/sacp";

app.post(
  "/mcp/invoke",
  withSacpX402({
    client: sacpClient,
    offering: await sacpClient.offering.fetch(provider.publicKey, slotId),
    cluster: "devnet",
    providerSigner,
    evaluatorSigner,
  }),
  async (req, res) => {
    const settle = req.sacp!;
    const result = await runYourAgent(req.body);
    await settle.session!.submitWork(await uploadToIpfs(result));
    res.json({ result });
  },
);

Client

import { sacpFetch } from "@telaro/x402/sacp";

const result = await sacpFetch(
  "https://my-agent.example.com/mcp/invoke",
  { method: "POST", body: JSON.stringify({ prompt }) },
  {
    sacp,
    client: buyerSigner,
    provider: providerSigner,
    evaluator: evaluatorSigner,
    workUri: "ipfs://buyer-spec",
  },
);
// result.response       the work response
// result.body           decoded JSON body if any
// result.session        the funded JobSession (caller can dispute / reclaim)
// result.accepted       true if autoAccept settled

Migrating from @telaro/sacp-x402? Change the import path. The function shapes (withSacpX402, sacpFetch) are unchanged.

Strategies on the roadmap

Today (1.0.0-beta.2):

  • trust — bond + score gate over a standard x402 payment.
  • sacp — sACP Job id is the payment proof (subpath @telaro/x402/sacp).

Coming in 1.0.0 stable:

  • sacp+trust — both: the buyer funds an sACP Job and the buyer wallet must clear the trust policy.
  • Unified expressX402Gate({ strategy: "sacp", ... }) overload that delegates to withSacpX402 so all three strategies share one API.
  • Deprecation shim publish of @telaro/[email protected] pointing at the subpath.

See docs/X402_UNIFICATION_PLAN.md for the full plan.

What this package is not

  • Not a payment verifier or settler. The trust gate runs and hands control back; compose it with any x402 stack.
  • Solana exact scheme only. Legacy transactions and versioned transactions without address lookup tables.
  • Not a replacement for @telaro/sdk. The gate is a thin client on top of the SDK; the SDK keeps the controller, PDA, and KeyLink helpers the gate consumes.

License

MIT.