feat(smoke): x402 gate proves a paywalled route answers 402, rejects forged payments and, with CHAIN, accepts a real one - #67
Open
ddpateltp wants to merge 9 commits into
Conversation
An unpaid request must answer 402 with a base64 PAYMENT-REQUIRED header; every accepts[] entry is graded against the Hedera exact scheme (scheme, CAIP-2 network, whole-unit amount, asset, payTo, extra.feePayer) and the recipe ceilings; a forged PAYMENT-SIGNATURE must be rejected. With pay: true and a CHAIN signer the gate builds the partially signed TransferTransaction the scheme specifies, pays once, and requires a successful PAYMENT-RESPONSE whose transaction credits payTo on the mirror node. Paying on mainnet is refused.
…in the runtime scope validators.x402 borrows the Playwright dev server, so the loader requires validators.playwright alongside it. Both run and validate execute the gate; x402 findings join commands and playwright in the runtime repair scope.
A typo in x402.yaml, or pay: true without chainValidation, used to surface only after GENERATE and a dev-server boot.
The fixture verifies the harness's payment the way a facilitator would: it deserialises the Hedera transaction and checks the transfer, so the paid probe is exercised end to end without testnet.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SMOKE x402 gate: prove a route is really paywalled
Problem
Ask a coding agent for "an x402-gated endpoint on Hedera" and you routinely get one of these back:
200to everyone (the paywall is not enforced),402with an x402 v1 JSON body and noPAYMENT-REQUIREDheader (no v2 client can pay it),PAYMENT-REQUIREDwhoseaccepts[]prices HBAR in decimals, nameseip155:296instead ofhedera:testnet, or has noextra.feePayer(the Hederaexactscheme MUST),200for anyPAYMENT-SIGNATUREheader, or crashes on a malformed one.The Playwright gate cannot see any of that: a JSON route that returns
200is a passing route, and the semantic validator only gets to look after SMOKE is green. Today the harness's only x402 knowledge is prose in the validator prompt, so a broken paywall is graded by an LLM reading a transcript, if at all.What this adds
validators.x402points at a small YAML on the SMOKE dev server. The gate speaks the protocol deterministically, in seconds, and every mistake becomes a finding the runtime repair prompt can act on:PAYMENT-REQUIREDheaderx402:route:<name>:status/:headerx402Version: 2,resource.url:decode/:version/:resourceaccepts[]entryexact, the recipe's CAIP-2 network, whole-unitamount,asset0.0.0or HTS id,payTo,extra.feePayer; recipe ceilingsmaxAmount/payTo/asset:requirementlisting each violated rulePAYMENT-SIGNATURE:tamperfacilitatorUrl(opt-in)GET /supportedlistsexacton that networkx402:facilitator:supportedpay: true(opt-in, needs CHAIN)PAYMENT-RESPONSE { success, transaction }, and the mirror node showspayTocredited exactlyamount:pay/:settlementThe paid probe builds the partially signed
TransferTransactionthe Hederaexactscheme specifies (payer topayTo,transactionId.accountId = extra.feePayer, payer signature only) using the@hiero-ledger/sdkthe harness already ships and the CHAIN signer it already funds. A green gate is therefore the "real paid request end to end", produced by the harness rather than read off an agent transcript. Paying onhedera:mainnetis refused at config load.doctorvalidates the gate config (and thatpay: truehas a signer) before a run;validateruns the unpaid probes;x402findings repair in the runtime scope alongside Playwright failures, andprompts/repair-runtime.mdtells the agent what a correct 402 looks like.How to run it
All 16 tests run fully offline. The test starts its own fixture resource server on a loopback port, needs no network access and no testnet credentials, and the signer is an ECDSA key generated inside the test. The
pay: truepath is exercised end to end against that fixture, which deserialises the Hedera transaction and checks the transfer the way a facilitator would, with the mirror-node lookup answered by a stubbedfetchImpl(a 404 first, then the settled transaction) so the settlement check is covered without a round trip to testnet.Before / after
Before: a generated
/api/quotethat returns200 {"quote":…}to an unpaid request passes SMOKE. After:x402 route /api/quote served the resource without payment (HTTP 200) — the paywall is not enforced, SMOKE fails in under a second, and the repair prompt names the exact rule. The test fixture covers seven distinct broken paywalls, each producing exactly one finding, plus a correct one that passes and a paid flow the fixture verifies the way a facilitator would.Related PRs
#44 adds a
doctorcheck that the x402 facilitator answers/supported, and #54 adds anx402Settlementflag that asks the validator prompt to look for settlement. Both improve preflight and prose. This PR is different in kind: it is a deterministic SMOKE gate that speaks the protocol against the dev server and fails with a named rule, so a broken paywall never reaches the LLM validator. The optionalfacilitatorUrlcross-check overlaps with #44 in intent; if #44 lands first the gate can defer to it.Scope notes
validators.x402are unchanged.ValidationFinding.categorygains"x402".validators.playwright(the dev server lives there); the loader says so.specs/transports-v2/http.mdandspecs/schemes/exact/scheme_exact_hedera.md.Built for ETHOnline 2026 (Hedera "Improve the Hedera Harness" track) while building an x402-gated agent marketplace on Hedera, where exactly these failures showed up in generated code.