@payperbyte/x402-middleware
Drop-in Foreseal Gate. Put it in front of any data endpoint and instantly get:
- USDC pay-per-call over x402 — unpaid request → HTTP 402 challenge; on payment, proxy upstream and return the bytes.
- A verify-before-act receipt — an EIP-712
PayloadAttestation over the exact bytes served, emitted as the X-BYTE-Attestation header. A buyer recomputes the hash and recovers the signer before acting.
- Delivery telemetry — latency / status / bytes / uptime per call.
- Optional discovery — a Bazaar discovery-extension + manifest entry so the wrapped endpoint surfaces in the x402 ecosystem under your name.
It generalizes the production PayPerByte x402 gateway. No new contracts. The receipt it emits is verifiable by the existing PayPerByte verifiers (the MCP server's verify, the SDK's verify, and the on-chain DataStreamLib) — same BYTE Library EIP-712 PayloadAttestation format.
Phase 1 scope. This is the telemetry pipe + a basic uptime/latency score. A calibrated quality SLA is gated on DQI and is not advertised here. There is no escrow, no staking/slashing, no on-chain fee splitter — those are out of scope by design.
Install
npm i @payperbyte/x402-middleware express
Quick start (Express)
import express from "express";
import { trustMiddleware } from "@payperbyte/x402-middleware";
const app = express();
app.use(express.json());
app.use(
"/quote",
trustMiddleware({
upstream: "https://my-api.internal/quote",
price: { perCallUsdc: "0.01" },
payTo: "0xYourUSDCAddress",
}),
);
app.listen(3000);
Set the delivery attester key in the environment:
export X402_MIDDLEWARE_ATTESTATION_KEY=0x<32-byte-hex>
That's it. GET /quote now returns 402 until paid, proxies your upstream on payment, and stamps every paid 200 with X-BYTE-Attestation.
Config (TrustMiddlewareConfig)
interface TrustMiddlewareConfig {
upstream: string;
price: { perCallUsdc: string }
| { perKBUsdc: string; floorUsdc: string };
payTo: Hex;
network?: "base" | string;
facilitatorUrl?: string;
attestation?: "delivery" | "provenance" | "off";
providerSigner?: Signer;
discovery?: { list: boolean; name: string; description: string; category: string };
schema?: object;
}
The two receipt tiers — and why the distinction matters
| delivery-integrity (default) | the PayPerByte middleware attester key | "PayPerByte delivered exactly these bytes at time T, tamper-evident in transit." The recovered signer is the PPB attester. |
| provenance (opt-in) | your key (providerSigner) | "This provider vouches these bytes are theirs." The recovered signer is you. |
We can't attest to the provenance of data we didn't produce — so delivery-integrity is sold as exactly what it is (a signed, timestamped receipt of what was served, tamper-evident). Bring your own key for the stronger provenance claim. Never market delivery-integrity as provenance.
import { privateKeyToAccount } from "viem/accounts";
trustMiddleware({
upstream,
price: { perCallUsdc: "0.05" },
payTo,
attestation: "provenance",
providerSigner: privateKeyToAccount("0x<your-key>"),
});
How a buyer verifies a receipt
The buyer reads the X-BYTE-Attestation header, recomputes keccak256(body), and recovers the EIP-712 signer — then asserts the signer is the attester they trust:
import { verifyReceipt, parseReceiptHeader } from "@payperbyte/x402-middleware";
const res = await fetch(url, { });
const body = await res.text();
const receipt = parseReceiptHeader(res.headers.get("x-byte-attestation")!);
const verdict = await verifyReceipt(body, receipt);
if (!verdict.verified) throw new Error(`do not act: ${verdict.reason}`);
if (verdict.recoveredSigner!.toLowerCase() !== EXPECTED_ATTESTER.toLowerCase()) {
throw new Error("unexpected signer — do not act");
}
This is the same two-leg check the deployed PayPerByte verifiers perform (keccak256(body) === payloadHash and recoverTypedDataAddress(...) === publisher), against the consensus-critical BYTE Library EIP-712 domain — so any existing PayPerByte verifier checks this receipt identically.
Fail-closed by design
- Facilitator unreachable → 503. Paid routes fail closed and are never served free while the x402 facilitator is down. The middleware retries the facilitator handshake in the background; until it's ready, every paid request gets a 503.
- Upstream error or schema violation → 502, no receipt. A paid buyer gets an honest error, never a signed payload that violates the advertised
schema.
Telemetry & score
const handler = trustMiddleware({ });
app.use("/quote", handler);
const score = handler.getScore();
Point telemetrySink at your own store (Postgres, the PPB indexer) to feed PQS/DQI. The score's note always states it is uncalibrated delivery telemetry, not a quality SLA.
Discovery
const handler = trustMiddleware({
upstream, price: { perCallUsdc: "0.01" }, payTo,
discovery: { list: true, name: "Acme Quotes", description: "Real-time quotes", category: "financial" },
});
app.get("/.well-known/x402.json", (_req, res) => {
res.json({ x402Version: 1, resources: [handler.manifestEntry("https://acme.example/quote")] });
});
Framework-agnostic core
Non-Express hosts can use the core directly: instantiate TrustEngine, gate it behind any x402 payment check, and call engine.fulfill() once payment is verified.
import { TrustEngine } from "@payperbyte/x402-middleware/core";
License
MIT © BYTEDev Inc.