PipRail
PipRail · Architecture

No Facilitator, No Custody, No Fee: How Backendless x402 Works

Most x402 implementations put a service in the middle of every payment. PipRail does not — and the reason it can get away with that is a single design decision: the merchant verifies the payment itself. Here is the architecture that makes backendless possible.

Tim Roelofs
Cofounder, PipRail
8 min read

When people picture an x402 payment, they usually picture three parties: a buyer, a seller, and a facilitator in between that settles the transaction. The facilitator is convenient — it abstracts the chain away — but look closely at what it actually is. It holds the funds in transit, which makes it a custodian. It usually takes a cut, which makes it a toll booth. And every payment depends on it being up and honest, which makes it a single point of failure. Three liabilities, bundled into one box that both sides have to trust.

PipRail’s central claim is that the box is unnecessary. None of the work a facilitator does actually requires a middleman to do it. The buyer can broadcast their own transfer. The seller can read that transfer and check it. Remove the box, and the custody, the fee, and the dependency all leave with it.

Two shapes for one payment
With a facilitator
Buyer → facilitator (custody + fee) → seller. A third party settles, holds the funds in transit, and can fail or charge.
Backendless
Buyer → seller, directly. The seller verifies the transfer against its own RPC. Nobody holds the funds; nothing sits in the middle.
Same protocol, same 402 handshake. The difference is whether anything stands between the two wallets.

Verification is local

Here is the part that surprises people: removing the facilitator does not weaken verification, because verification never needed a third party in the first place. A blockchain is already a shared, public source of truth. When a buyer pays, the seller reads the transaction directly from its own node — viem for EVM chains, the native client for each non-EVM family — and confirms it matches the exact payment it asked for. There is no server to run and no database to provision; the gate computes everything it needs from the chain and from the challenge it issued.

This is not PipRail bending the rules. The x402 v2 specification, in §7, explicitly says resource servers may host the verification endpoints themselves rather than handing them to a facilitator. The backendless shape is a configuration the standard anticipates and blesses. We just took it as the default instead of the exception.

The trick that makes proofs unforgeable

A payment proof has to be cryptographically bound to the specific challenge it answers, or it could be replayed or forged. PipRail binds proofs two ways depending on the chain: for some families the challenge nonce rides in a memo or note and verification matches it on the merchant’s own account; for others the proof is the transaction hash itself, verified by reading the transaction plus a recency window and a single-use proof set.

But the load-bearing detail is smaller and easier to miss. Verification always re-derives every field it checks — amount, asset, recipient, network — from the trusted payment requirement the gate issued, never from the client-supplied reference. A malicious client can echo back anything it likes; it does not matter, because verify() never reads its own truth from the echo. A forged or altered proof has nothing legitimate to point at, and replayed proofs are caught by the single-use set. The honesty of the check does not depend on the honesty of the payer.

One contract, ten families, mirrored

Reaching 29 chains without the core turning into a tangle takes discipline, and the discipline has a name: the PaymentDriver abstraction. The protocol layer — the payment gate, the client, the wire envelopes — depends only on that one contract. It imports no chain libraries at all: no viem, no @solana/web3.js, nothing chain-specific. Each chain family is a self-contained driver behind the contract, and the families mirror each other file-for-file.

drivers/
  evm/       chains · wallet · pay · verify · index
  solana/    chains · wallet · pay · verify · index
  near/      chains · wallet · pay · verify · index
  stellar/   chains · wallet · pay · verify · index
  xrpl/      chains · wallet · pay · verify · index
  ...        (one self-contained folder per chain family)

Adding a chain family means implementing the same five files and registering the driver — the core never moves. It also means the non-EVM drivers are optional peers that load only when you name one of their chains, so a pure-EVM install carries none of their weight. Breadth on the outside, a small unchanging contract on the inside.

Replay protection you can make durable

Out of the box, the used-proof set lives in memory, which is exactly right for a single instance. Run several, and you want them to share one view of which proofs have been spent — so the store is pluggable. Hand the gate an isUsed and a markUsed backed by Redis or a database, and every instance reads from the same set. The protocol logic does not change; only where “seen” is recorded does.

requirePayment({
  chain: 'base', token: 'USDC', amount: '0.01', payTo,
  // Default replay protection is in-memory. Make it durable across instances:
  isUsed:  (ref) => redis.sIsMember('piprail:proofs', ref),
  markUsed: (ref) => redis.sAdd('piprail:proofs', ref),
})

Why this is the whole product

It is tempting to add a backend. A backend is where dashboards live, where analytics accrue, where a fee can be inserted. But the moment a payment routes through your server, you are a custodian and a point of failure, and the thing you built stops being a tool the developer owns and starts being a platform they depend on. PipRail’s architecture is a standing refusal to cross that line. No backend, no database, no account, no fee — not because we couldn’t build them, but because the absence is the point. The rail should be something you own, not a platform you join. Verifying your own payments, against your own node, with nobody in the middle, is what owning it looks like.

Share Post on X
Tim Roelofs
Cofounder, PipRail

Cofounder of PipRail — the open, no-fee x402 rail for AI agents, across 29 chains. Writing about the architecture of self-custody payments, and why the rail should have no middle.

Frequently asked

What is a facilitator in x402, and why would you avoid one?

A facilitator is a third-party service that sits between the buyer and the seller: it settles the payment, often takes custody of the funds in transit, and usually charges a cut. It also becomes a single point of failure and a party both sides must trust. PipRail avoids it because none of that work actually requires a middleman — the buyer can broadcast their own transfer and the seller can verify it directly. Removing the facilitator removes the custody, the fee, and the dependency in one move.

How does PipRail verify a payment without a backend or database?

Verification is local. The seller reads the transaction straight from their own RPC node — viem for EVM chains, the native client for each non-EVM family — and checks it against the exact payment requirement they issued. Replay protection is an in-memory set of already-seen proofs plus a recency window. There is no server to run, no database to provision, and no external service in the path; everything the gate needs, it computes itself.

Can a payment proof be replayed or forged?

No. Every proof is single-use: the gate records each settled proof and rejects a second presentation, within a recency window that bounds how long a proof is even considered. And it cannot be forged against, because verify() re-derives every field it checks — amount, asset, recipient, network — from the trusted payment requirement it issued, never from the client-supplied echo. A forged or altered proof has nothing legitimate to point at.

What happens if I run multiple server instances?

The in-memory used-proof set is per-process, which is fine for a single instance. For a multi-instance deployment, the replay store is pluggable: pass isUsed and markUsed callbacks backed by Redis, a database, or any shared store, and all instances share one view of which proofs have been spent. The protocol logic does not change — only where the “seen” set lives.

Is backendless verification actually allowed by the x402 spec?

Yes. The x402 v2 specification (§7) explicitly says resource servers may host the verification endpoints themselves rather than delegating to a facilitator. So PipRail’s backendless shape is a first-class, spec-supported configuration — not a clever workaround or a deviation from the standard.

How does the driver architecture keep the protocol layer chain-agnostic?

The protocol layer — the payment gate, the client, the wire envelopes — depends only on a PaymentDriver contract, with zero imports of viem, @solana/web3.js, or any chain library. Each chain family is a self-contained driver that implements that contract, and the families mirror each other file-for-file: chains, wallet, pay, verify, index. Adding a new family means implementing the same contract and registering it — the core never changes, and a pure-EVM install never downloads the non-EVM drivers.

No middle. No fee. Yours.

PipRail is the open, self-custody x402 rail — verify your own payments, against your own node, with nobody in between.

More writing