From 3e70a5483822123927d86755d69029cd58ff0151 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Wed, 25 Feb 2026 13:11:50 -0700 Subject: [PATCH] docs(x402): document restricted auth, dry-run, and replay semantics --- pages/developers/blueprint-runner/x402.mdx | 146 +++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 pages/developers/blueprint-runner/x402.mdx diff --git a/pages/developers/blueprint-runner/x402.mdx b/pages/developers/blueprint-runner/x402.mdx new file mode 100644 index 00000000..2df2879a --- /dev/null +++ b/pages/developers/blueprint-runner/x402.mdx @@ -0,0 +1,146 @@ +--- +title: x402 Payment Gateway +description: Accept x402 payments over HTTP and translate them into paid job executions inside a Blueprint Runner. +--- + +import GithubFileReaderDisplay from "/components/GithubFileReaderDisplay"; + +# x402 Payment Gateway + +SDK source (GitHub): https://github.com/tangle-network/blueprint/tree/main/crates/x402 + +x402 is an optional, off-chain payment ingress path for job execution. It exposes HTTP endpoints that require a valid +x402 payment (typically stablecoins on an EVM chain) and then injects a `JobCall` into the Blueprint Runner after the +payment is verified and settled by an x402 facilitator. + +This is useful when you want a service to accept payments from clients on EVM chains without requiring the client to +submit on-chain Tangle transactions for each job. + +## How It Fits In the Runner + +`X402Gateway` runs as a `BackgroundService` and `X402Producer` is the paired producer that converts verified payments +into `JobCall`s. + +Quick start (from the crate docs): + + + +Each x402-originated `JobCall` includes metadata for correlation and accounting: + +- `X-X402-ORIGIN` and `X-X402-QUOTE-DIGEST` +- `X-X402-PAYMENT-NETWORK` and `X-X402-PAYMENT-TOKEN` +- `X-TANGLE-SERVICE-ID` and `X-TANGLE-CALL-ID` (synthetic, for correlation) + +## Enabling It (Blueprint SDK) + +If you are using `blueprint-sdk`, enable the feature flag: + +```toml +[dependencies] +blueprint-sdk = { version = "*", features = ["x402"] } +``` + +You can also depend on `blueprint-x402` directly. + +## Configuration (TOML) + +The operator configures: + +- where the gateway listens (`bind_address`) +- which facilitator to use (`facilitator_url`) +- which tokens and chains are accepted (`accepted_tokens`) +- conversion rates from wei-denominated job prices into token amounts (`rate_per_native_unit`, plus optional `markup_bps`) +- default x402 exposure policy (`default_invocation_mode`) +- per-job invocation and auth policies (`job_policies`) + + + +## Job Pricing Input + +The x402 gateway needs a per-job price map in wei: + +- key: `(service_id, job_index)` +- value: `price_wei` + +This is the same shape as the pricing engine job pricing config, and it is intentionally explicit. If a job is not in +the map, the gateway returns `404` for that job. + +## Invocation and Authorization Model + +x402 job exposure is explicit and policy-driven per `(service_id, job_index)`. + +Invocation modes: + +- `disabled`: job cannot be invoked via x402 +- `public_paid`: payment-gated, otherwise public +- `restricted_paid`: payment-gated plus caller authorization and on-chain parity checks + +Restricted auth modes: + +- `payer_is_caller`: settled payer is treated as caller +- `delegated_caller_signature`: caller is asserted via signed headers + +For restricted jobs, the gateway performs an on-chain parity check with: + +- `isPermittedCaller(service_id, caller)` via `eth_call` + +Delegated mode headers: + +- `X-TANGLE-CALLER` +- `X-TANGLE-CALLER-SIG` +- `X-TANGLE-CALLER-NONCE` +- `X-TANGLE-CALLER-EXPIRY` + +Delegated signed payload format: + +```text +x402-authorize:{service_id}:{job_index}:{keccak(body)_hex_no_0x}:{nonce}:{expiry_unix_secs} +``` + +## HTTP Endpoints + +The gateway exposes: + +- `GET /x402/health`: returns `ok`. +- `GET /x402/stats`: lightweight counters for accepted, denied, replay-denied, enqueue-failed, and dry-run outcomes. +- `GET /x402/jobs/{service_id}/{job_index}/price`: discovery endpoint that returns the wei price and settlement options + (no payment required). Returns `403 x402_disabled` if job exists but is disabled for x402. +- `POST /x402/jobs/{service_id}/{job_index}`: paid job execution. The x402 middleware returns `402` with payment + requirements if no valid payment is provided. After payment settles, the gateway returns `202 Accepted` and injects a + `JobCall` into the runner. +- `POST /x402/jobs/{service_id}/{job_index}/auth-dry-run`: runs restricted auth + `eth_call` policy check without + enqueueing work or settling payment. + +## Receipt and Replay Semantics + +- `202 Accepted` means the paid request was accepted and enqueued, not that the job has completed. +- In delegated restricted mode, duplicate nonce reuse in the same caller/job scope is rejected in paid flow with + `409 signature_replay`. +- `auth-dry-run` is non-mutating and does not consume delegated nonces. + +## Relationship to RFQ and the Pricing Engine + +Tangle’s on-chain RFQ path is `submitJobFromQuote`. Separately, operators can expose x402 as an off-chain settlement +path. + +The pricing engine proto includes optional x402 fields (`settlement_options`, `x402_endpoint`) in `GetJobPriceResponse`. +At time of writing, enabling those fields requires constructing `PricingEngineService` programmatically with +`with_x402_settlement`, since the `pricing-engine-server` binary does not expose x402 flags yet. + +## Operational Notes + +- Keep `facilitator_url` pinned to a trusted HTTPS endpoint. +- Keep conversion rates current. Stale `rate_per_native_unit` values create user-facing price surprises. +- Treat the HTTP body as untrusted input and validate it inside the job handler. +- For restricted jobs, pin `tangle_rpc_url` and `tangle_contract` to stable production infrastructure. +- Use `auth-dry-run` before enabling restricted jobs to verify caller policy parity and signature wiring.