x402 Conformance

Prove your x402 payments can't double-credit, credit too early or credit on a broken answer, in one CI step.

Experimental. An open, Apache-2.0 set of conformance and safety checks for x402 V2 facilitators and resource servers, starting with Solana. It complements x402 and its facilitators and doesn't replace them. It never signs, pays or touches mainnet, and live mode only sends GET /supported. Passing a check is evidence about one property; it doesn't certify conformance to x402.

Run it

GitHub Action. Save as .github/workflows/x402-conformance.yml:

name: x402 conformance
on: [push, pull_request]
jobs:
  x402:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: atsp-protocol/x402-conformance@v1
        with:
          facilitators: https://x402.org/facilitator   # GET /supported only

With no inputs it runs only the F1-F9 safety suite. To test your own resource server, add resource-url (your paid route) and start (the command that starts your server, which receives X402_FACILITATOR_URL). The Action writes JSON results, a SARIF 2.1.0 file for GitHub code scanning and a JUnit XML report; every failure links to its fix in docs/FIXES.md.

One command, nothing to install (with pipx or uv):

pipx run --spec x402-conformance x402conf offline
uvx --from x402-conformance x402conf live https://x402.org/facilitator

CLI (Python 3.10+, no dependencies):

pip install x402-conformance
x402conf live https://x402.org/facilitator   # S1-S4, GET /supported only
x402conf offline                             # F1-F9 against reference and fault-injected doubles
x402conf report --dir DIR --sarif out.sarif --junit junit.xml

What it checks

idfails when
Live, read-only (GET /supported)
S1the response is missing kinds, extensions or signers
S2a V2 network isn't written as a CAIP-2 ID
S3a Solana kind uses an old V1 name such as solana-devnet
S4a Solana exact kind has no valid fee payer, or one that isn't a listed signer
Safety: against doubles, on devnet and in your resource server
F1a malformed payment is accepted instead of rejected
F2an underpayment or dust amount is accepted
F3the same payment is settled, or served, twice
F4a used memo or nonce is replayed in a new transaction
F5a payment counts as settled before it's confirmed, or settlement_pending comes without a transaction
F6a facilitator timeout or garbage answer turns into a credit
F7a pending payment retried with the same body never reaches its final result, or is credited more than once
F8a payment that failed on chain stays "pending" instead of failing
F9a 503, an HTML page or a truncated /settle answer is credited, or the payment can't be retried
R0-R9your resource server serves paid content under any of these faults (TypeScript runner in ts/, for servers on @x402/core and @x402/svm)

Results

Live, 2026-10-20 (GET /supported only)

facilitatorS1S2S3S4
x402.dexter.cashpasspasspasspass
facilitator.payai.networkpasspasspasspass
x402.org/facilitatorpasspasspasspass

Raw bodies and hashes: evidence/2026-10-20.

Devnet, 2026-10-20 (F1-F6 over real /verify and /settle)

Against my self-hosted reference facilitator (@x402/core 2.28.0 with the @x402/svm 2.28.0 exact scheme) on Solana devnet, using a throwaway test token and throwaway keypairs. No mainnet, no real funds.

checkresulton chain (devnet explorer)
F1 malformed payload fails closedpassnone, /verify only
F2 dust amount is rejectedpassnone, /verify only
F3 the same payload settled twice succeeds oncepass3Z6SFs…
F4 a used memo in a new transaction is rejectedfail2L3Jxe…, 2U31kB…
F5 success only after confirmationpass3GyFs6…
F6 a timeout or garbage answer never creditspasssee run.json

About F4. The reference facilitator deduplicates by transaction (F3 passes) but doesn't track the memo across different transactions, so two different transactions carrying the same memo both settled. Each was a real payment signed by the payer, so the seller was paid twice rather than losing money, but a resource server that binds one purchase to one memo has to reject the second payment itself. The result is recorded as it came out.

Every fault-injected build (open_on_malformed, accept_dust, success_unsent) was caught by the check it targets. Setup transaction: NoU2pE…. Full results and hashes: evidence/devnet.

Free and open core

Apache-2.0. Every check, the Action, the devnet runner and the Lean 4 models of the credit rule are free, forever. Free run for your project: open an issue with your repo or facilitator URL and I'll run the suite, post the results and send a tests-only pull request with the Action and badge.

Managed services

Optional, on top of the free suite: hosted continuous monitoring of your live endpoints, signed conformance reports per release, custom checks for your scheme or chain, and priority support. Details in docs/MANAGED.md; get in touch through a Managed services enquiry or data@optitransfer.ch.