Book402 runs its own x402 facilitator — no dependency on Coinbase CDP or any third party.
What Is a Facilitator?¶
In the x402 protocol, a facilitator is a server that:
- Verifies payment signatures (checks that the agent signed a valid USDC transfer)
- Settles payments (submits the actual USDC transfer on-chain)
graph TD
subgraph External["❌ External Facilitator (CDP)"]
A1[Your API] -->|"verify/settle"| B1[Coinbase CDP]
B1 -->|"needs API key"| C1[Rate limits]
C1 --> D1[Third-party dependency]
end
subgraph SelfHosted["✅ Self-Hosted (Book402)"]
A2[Book402 API] -->|"verify/settle"| B2[x402-rs on localhost]
B2 -->|"direct RPC"| C2[Base L2]
C2 --> D2[Full control]
end
style SelfHosted fill:#0a1a0a,stroke:#33aa33
style External fill:#1a0a0a,stroke:#aa3333
Why Self-Host?¶
| Option | Supports Mainnet | Auth Required | Rate Limits | Control |
|---|---|---|---|---|
| x402.org | ❌ Sepolia only | ❌ | ? | ❌ |
| Coinbase CDP | ✅ | ✅ API key | 1K/mo free | ❌ |
| Self-hosted | ✅ | ❌ | None | ✅ |
Our Setup¶
We use x402-rs — a production-grade Rust implementation:
- Image:
ghcr.io/x402-rs/x402-facilitator:latest - Runtime: Docker on QuimServer
- Port:
127.0.0.1:8402(not exposed to internet) - Networks: Base mainnet + Base Sepolia
Docker Setup¶
# config.json
{
"port": 8080,
"host": "0.0.0.0",
"chains": {
"eip155:8453": {
"eip1559": true,
"signers": ["0xYOUR_PRIVATE_KEY"],
"rpc": [{ "http": "https://mainnet.base.org", "rate_limit": 50 }]
}
},
"schemes": [
{ "id": "v1-eip155-exact", "chains": "eip155:*" },
{ "id": "v2-eip155-exact", "chains": "eip155:*" }
]
}
docker run -d \
--name x402-facilitator \
--restart unless-stopped \
-v ./config.json:/app/config.json:ro \
-p 127.0.0.1:8402:8080 \
ghcr.io/x402-rs/x402-facilitator:latest
Verify It's Running¶
{
"kinds": [
{ "x402Version": 1, "scheme": "exact", "network": "base" },
{ "x402Version": 2, "scheme": "exact", "network": "eip155:8453" }
],
"signers": {
"eip155:8453": ["0x6B1925e4a1f779797eF51A18A4694B59FFb60Aba"]
}
}
Integration with Express¶
The API server connects to the facilitator via HTTPFacilitatorClient:
import { paymentMiddleware, x402ResourceServer } from '@x402/express';
import { HTTPFacilitatorClient } from '@x402/core/server';
import { ExactEvmScheme } from '@x402/evm/exact/server';
const facilitatorClient = new HTTPFacilitatorClient({
url: 'http://127.0.0.1:8402' // Local facilitator
});
const resourceServer = new x402ResourceServer(facilitatorClient)
.register('eip155:8453', new ExactEvmScheme());
// Define paid routes
const paidRoutes = {
'GET /search/hybrid': {
accepts: { scheme: 'exact', price: '$0.01', network: 'eip155:8453' },
payTo: '0x6B1925e4a1f779797eF51A18A4694B59FFb60Aba',
description: 'Hybrid BM25+vector book search'
}
};
app.use(paymentMiddleware(paidRoutes, resourceServer));
Facilitator API¶
The facilitator exposes these internal endpoints:
| Endpoint | Method | Description |
|---|---|---|
/supported |
GET | List supported payment kinds and networks |
/verify |
POST | Verify a payment signature |
/settle |
POST | Settle a verified payment on-chain |
Security
The facilitator listens on 127.0.0.1 only — it's never exposed to the internet. Only the Book402 API server talks to it.