Skip to content

sF1nX/x402station-middleware

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

x402station-middleware

Drop-in fetch wrapper that calls x402station.io /preflight before every paid x402 request your agent makes, and refuses (throws) when the endpoint is flagged dangerous.

If your agent already wraps fetch with @x402/fetch, this is one line of code:

import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import { wrapWithPreflight } from "x402station-middleware";

const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as `0x${string}`);

const x402Fetch = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});

// One-line shielding: preflight runs automatically before every call.
// Second arg is options (all optional); the wallet binding lives inside
// `x402Fetch`, the middleware itself doesn't need an account.
const safeFetch = wrapWithPreflight(x402Fetch);

// Use exactly like fetch.
const r = await safeFetch("https://api.example.com/x402-endpoint", {
  method: "POST",
  body: JSON.stringify({ ... }),
});
// ↑ throws PreflightBlockedError if the endpoint is decoy / zombie / dead.

Why

x402 lets agents pay HTTP endpoints, but the network has shape problems:

  • ~161 endpoints listed at ≥ $1000 USDC (decoy / honeypot — most are anti-scraper "swarm" routes)
  • ~10 services 100 % erroring in the last hour but still listed (zombies)
  • Catalog concentration (one provider can be > 40 % of the catalog)

x402station independently probes every endpoint on agentic.market every 10 minutes and flags these. This middleware makes those signals automatic — your agent code doesn't have to remember to preflight, the wrapper does it.

Install

npm install x402station-middleware
# or
pnpm add x402station-middleware
# or
bun add x402station-middleware

Peer deps (must already be installed in your agent project): @x402/fetch, @x402/evm, viem.

API

wrapWithPreflight(paidFetch, options?)

Returns a fetch-shaped function that calls /api/v1/preflight on every URL before forwarding.

Option Type Default Description
refuseOn string[] ["dead", "zombie", "decoy_price_extreme"] Warnings that cause refusal. Pass [] to disable refusal (preflight still runs, but pass-through).
cacheTtlMs number 300_000 (5 min) Per-URL preflight cache. Matches x402station's internal probe cadence (10 min) — same window, identical data. Set 0 to disable.
creditId string (uuid) Bulk-credit id from POST /api/v1/credits ($0.50 = 1000 prepaid preflights). When set, preflight calls go via X-Credit-Id header instead of paying $0.001 each → effective $0.0005/preflight.
failOpen boolean false Forward the request when preflight itself fails (network / 503 / timeout). Default fail-closed.
preflightTimeoutMs number 10_000 Per-call preflight timeout. Clamped to [1_000, 30_000].
baseUrl string https://x402station.io Override (testing). Only canonical x402station.io or localhost / 127.0.0.1 / [::1] accepted; any other host throws at wrap time.

Error classes

import { PreflightBlockedError, PreflightUnavailableError } from "x402station-middleware";

try {
  await safeFetch(url);
} catch (e) {
  if (e instanceof PreflightBlockedError) {
    console.log("blocked:", e.warnings, e.metadata);
    // → e.warnings: ["zombie", "decoy_price_extreme", ...]
    // → e.metadata: { url, service, provider, price_usdc, ... }
  }
  if (e instanceof PreflightUnavailableError) {
    console.log("preflight down:", e.cause);
  }
}

Cost

  • Per call: $0.001 USDC (the standard x402station preflight tier).
  • With creditId: $0.0005 USDC effective ($0.50 / 1000 calls). Buy via POST /api/v1/credits.
  • Cached calls: free.

For an agent making 100 paid x402 calls per day, that's ~$0.10/day in preflights, often offset by the very first decoy you avoid (a single $1000 honeypot would have wiped your wallet).

Networks

Base mainnet (eip155:8453) — the network x402 lives on. Base Sepolia (eip155:84532) for testing. The wallet you pass into wrapFetchWithPaymentFromConfig must hold USDC; preflight settles the same way every other paid x402 call does.

Caveats / design choices

  1. Default fail-closed. If preflight is unreachable (network blip, 503, timeout), the call throws PreflightUnavailableError. Override with failOpen: true only if availability matters more than safety.
  2. Refusal is on warnings, not just ok. We throw if any warning in refuseOn is in the response, OR if the server flipped ok to false. This is intentional belt-and-suspenders — keeps you safe even if the server's ok calculation changes.
  3. Self-requests skip. Calls TO x402station.io itself (preflight, forensics, credits, etc.) are forwarded directly without recursing.
  4. Cache is per-instance. Re-instantiating wrapWithPreflight gives you a fresh cache. There's no global cache; if you want one, share the wrapped fetch.
  5. Bearer-token allow-list on baseUrl. Only https://x402station.io or local-dev hosts pass. Mirrors the canonical-host check in x402station-mcp — a misconfigured agent can't be tricked into routing preflight payments off-target. CodeRabbit findings (mastra-ai/mastra#15804).

Companion

Same family as the official MCP adapter x402station-mcp, the AgentKit x402station action provider, the @lucid-agents/x402station client, and the upcoming standalone Mastra package. All share signals + canonical host allow-list + Greptile/CodeRabbit security fixes.

License

MIT.

About

Drop-in fetch wrapper that auto-preflights x402 calls. Refuses decoy/zombie/dead endpoints automatically.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors