← Blog
September 4, 2026 · 9 min

Agentic week: x402's fixes came from outside

Thirty-seven pull requests merged into x402 between 28 August and 4 September. Seventeen of them came from accounts outside the maintainer set, and almost all of those were corrections.

Last week the theme was time: every substantive change moved part of the payment out of the single HTTP round trip. This week the theme is who is doing the work. The core team shipped features — a delegated authorizer for SVM upto, a facilitator latency pass, canonical contract addresses. Everyone else shipped bugs they found by reading the code.

That distribution matters more than any individual patch. A payment rail is only as trustworthy as the number of independent people who have tried to break it. Counting by author on the merged set, twelve PRs came from phdargen, three from CarsonRoscoe, two from PhilBot402 and three from the docs bot. The remaining seventeen came from thirteen distinct outside accounts. The releases carrying the week are @x402/core 2.25.0 on npm, published 3 September, and x402 2.22.0 on PyPI, published 4 September. For scale: @x402/core recorded 997,660 npm downloads in the thirty days ending 29 August.

1. The Java SDK was serving paid content before it got paid

PR #3074 merged on 4 September, six files, 277 lines added. It closes issue #3068, filed on 6 August by Sertug17: PaymentFilter delivers content before settlement.

The mechanism is the one we mapped when we audited the two-phase gap. Java's PaymentFilter.doFilter() called chain.doFilter() against the real HttpServletResponse. The protected handler wrote its output — and in a servlet container, usually committed it — before facilitator.settle() ran. If settlement then failed on an RPC timeout or a facilitator outage, the buyer already had the paywalled bytes and the seller had nothing.

There was a fallback. respond402() fired on settlement failure. But it was only reachable while response.isCommitted() was still false, which after a handler has written a body it usually is not. The safety net was conditional on a race the SDK did not control.

The detail worth reading twice — the contributor also had to fix FilterIntegrationTest. Its stub facilitator returned a default SettlementResponse with success = false, and its validHeaderGets200 test was passing because of the bug: the content was already delivered before the always-failing settle could matter. The test suite was not merely failing to catch the defect. It was ratifying it.

The fix adds a BufferingHttpServletResponseWrapper. Status codes and headers still forward to the real response immediately; the body accumulates in memory and is flushed only after settlement succeeds. On failure the buffer is discarded and a 402 goes out, deterministically rather than conditionally. The submitted evidence is the part that should be standard practice: the new integration test was run against pre-fix code and reproduced the bug as expected 402 but was 200 with a leaked body.

The TypeScript Express middleware in the same repository had buffered correctly for a long time. The Java port never got the equivalent. That is the real lesson: in a multi-language protocol SDK, the reference implementation's guarantees are not inherited. They are re-implemented, and each re-implementation is a fresh opportunity to lose money.

2. A hardening wave, one reader at a time

The Java fix was not isolated. Four more outside contributors landed defects in the same window, each found by reading rather than by failing in production.

PR #2973 bounds HTTP response body reads in the Go SDK. The payment client, the facilitator client and the Bazaar client all called io.ReadAll directly, so a facilitator or resource server could make the SDK buffer an arbitrarily large response before it validated the status or decoded any JSON. The fix caps reads at 4 MiB plus one byte and exports ErrResponseBodyTooLarge. Oversized bodies are closed without draining the remaining untrusted data.

PR #2962 is smaller and worse. ResolveSettlementOverrideAmount parsed the integer part of a percentage into an int64, ignored parse errors, then multiplied by 100. Large values overflowed into a negative or zero settlement amount.

PR #3051 changes @x402/core so buildPaymentRequirements() throws when no scheme server is registered for the requested scheme and network. It used to log a console.warn and resolve with an empty array, which produced a 402 telling the client that payment was required while offering zero ways to pay it. The same function already threw fifteen lines further down when the facilitator did not advertise the scheme. Twenty-one tests across @x402/express, @x402/fastify, @x402/hono and @x402/next depended on the lenient path.

PR #3282 makes the Go EIP-3009 client use now + paymentRequirements.maxTimeoutSeconds for validBefore instead of a hardcoded one hour. The Permit2 path and the TypeScript SDK already did. Authorization lifetime is a security parameter, and the Go client was ignoring the server's stated value.

Two more corrections rounded out the surface. EXTENSION-RESPONSES was being decoded only to console.log allowlisted fields and then discarded, so resource servers could not branch on extension outcomes after verify or settle; #3278 in TypeScript, #3306 in Python and #3301 in Go now surface it as a server-internal sidechannel, deliberately kept out of the buyer-facing PAYMENT-RESPONSE. And issue #3299 — a v2 client could set the onchain app attribution field a that the resource server had never declared — was closed across all three SDKs, making the resource server the authority for that field.

3. upfront got its transfer method families, and Lightning got a door

PR #3145 merged on 2 September. One file, thirty-seven lines added to specs/schemes/exact/scheme_exact.md. It is the smallest change of the week and probably the most consequential.

Last week the upfront payment flow landed — settle before the handler runs. That PR shipped the machinery and deliberately left the network-agnostic properties of client-prepaid methods unspecified. #3145 fills that gap with two asset transfer method families:

// signed-transaction
// Client signs, facilitator submits during settle.
// Replay safety inherited from the network. No facilitator-side dedup.
// Stateless. Preferred where network and payer tooling permit.

// payment-proof
// Client executes the payment, presents a proof.
// settle validates the proof and binds it. Requires a consumed-proof store.
// Stateful. Atomic single-use claim before resource execution.

The naming argument is the interesting part. The prior discussion had settled on transaction-proof. The author kept that as a valid value but named the family payment-proof, specifically to fit Lightning: a Lightning proof is a payment preimage, checked as sha256(preimage) == payment_hash. There is no transaction, no transaction hash, and nothing to look up onchain. Calling the family transaction-proof would have made every shared requirement read as onchain-specific and pushed Lightning back out of the structure.

That is a deliberate act of consolidation. Four separate Lightning drafts are open, each written as its own exact scheme, plus three more proposals in the same shape. One flow, one transfer-method discriminator per mechanism, shared requirements in one file. When we audited L402, the gap was that Lightning-native payment had no seat inside the x402 scheme structure. This is that seat being built.

4. The core team's week: delegation, latency, canonical addresses

PR #3346 (TypeScript, 26 files, +1,287/−146) and #3347 (Go) landed on 3 September. SVM upto can now delegate the receiverAuthorizer role to the facilitator, the way EVM batch-settlement already could. A server that omits receiverAuthorizerSigner no longer needs a voucher-signing key at all. The facilitator signs the claim voucher only after binding the deposit to a caller identity and matching that same identity on claim — and a lost binding fails closed. Self-managed server vouchers stay the default. Settle routing prefers a server-stamped payload.type of deposit or claim, falling back to the existing inference when absent, so non-delegating servers keep working.

PR #3355 is a four-part latency pass on the Go facilitator. Parallel verify simulation for EVM exact and Permit2 — opt-in and defaulting to off, because the tradeoff is one wasted eth_call per payment rejected on signature grounds. A network-scoped asset-contract cache, fifteen minutes, 4,096 entries, positive results only so a token mid-deployment self-heals. Reuse of verify's payer GetCode lookup in the ERC-6492 settle branch instead of fetching it twice. And linear backoff for SVM channel-account reads: 200/400/600/800/1000ms across six reads, replacing 200/400/800/1600ms across five, same three-second budget but a maximum single wait of one second rather than 1.6 — roughly 2.5 Solana slots instead of four.

Meanwhile PR #3354 pointed the TypeScript and Go clients at the canonical v1.1 commerce contract deployments, closing out the auth-capture rewrite from last week. Two facilitator-directory changes are worth noting: an FTP Canton facilitator was listed on 2 September, live on Canton mainnet with Canton Coin and CIP-56 tokens, and the Stellar mechanism learned to accept CAP-71 V2 address credentials for Protocol 28.

5. A2A moved in with MCP

On 27 August the A2A project announced that Agent2Agent had been accepted as a Growth Stage project at the Agentic AI Foundation. The post frames the split cleanly: MCP is the vertical integration layer connecting agents to tools and databases; A2A is the horizontal protocol for peer-to-peer collaboration. Sibling projects under the Linux Foundation-directed AAIF now include MCP, goose and AGENTS.md.

This is not an ownership event. A2A was already in neutral governance; it moved into a narrower home. But the consolidation is directionally significant for anyone building on both, because it puts the tool-access protocol and the agent-to-agent protocol under one roadmap process. We audited A2A v1.0 when the breaking changes landed in March; the governance question we flagged then now has an answer.

On the MCP side, the Enterprise Interest Group charter merged on 29 August. Its scope statement is unusually disciplined for a charter: the group's mandate is surfacing protocol-level requirement gaps and handing them to Working Groups, and writing or owning SEPs is explicitly out of scope. The in-scope list reads like a threat model — identity propagation across spawned and delegated agents, identity lineage, least-privilege for child agents, descendant revocation, and audit evidence for SOC 2, HIPAA, GDPR and the EU AI Act. It also acknowledges that the 2026-07-28 stateless core changed the gateway problem from session affinity to header and context propagation across proxies.

What it means for LLM4Agents

The Java bug is the one to internalize, because LLM4Agents is a seller. Every x402 seller integration has the same shape: verify, run the work, settle, respond. The failure mode is not exotic — it is a settlement that fails after the response body is already on the wire. For a gateway that mediates model inference, the exposure is worse than for a static paywall, because the expensive thing has already been bought from an upstream provider by the time settlement fails. The correct posture is that no paid byte leaves the process until settle returns success, and the correct evidence is a test that fails against the pre-fix code.

The second lesson is about SDK provenance. LLM4Agents does not have to ship a Java integration to be affected — it has to be integrated by one. A buyer agent running the Go SDK before #3282 was signing EIP-3009 authorizations valid for an hour regardless of what our maxTimeoutSeconds said. A buyer on @x402/core before #3051 could receive a 402 with an empty accepts list from a misconfigured seller and have no idea why. Buyer-side SDK version skew is a support surface, and it is one we can measure.

payment-proof is the more interesting medium-term signal. If a Lightning mechanism lands inside exact rather than beside it, a gateway that already speaks upfront gets a second settlement network almost for free — different finality, different cost curve, same scheme. That is exactly the kind of optionality a routing layer should want.

A2A joining AAIF is neutral-to-positive. Consolidated governance makes the A2A x402 extension a safer thing to build against, because the payment extension and the protocol it extends are no longer in separate political orbits.

Staying on the frontier

Concrete, in order.

First, write the free-riding test. Not a review, a test. Stand up a facilitator stub that always verifies and always fails to settle, point it at a paid inference route, and assert that zero bytes of completion reach the client. Run it against current code before writing any fix. If it passes on the first try, the assertion is wrong.

Second, pin and publish buyer SDK floors. Document the minimum @x402/core, Go, Python and Java versions our gateway is validated against, and state what breaks below each line — validBefore semantics under the Go floor, the empty-accepts path under the TypeScript one. Buyers cannot upgrade what they do not know is broken.

Third, bound every untrusted read. #2973 is a four-line lesson: any ReadAll against a facilitator or a resource server is a memory-exhaustion primitive. Audit our own client paths for the same shape and cap them.

Fourth, treat the resource server as the authority on server-owned fields. The builder-code attribution bug generalizes: any field a client can set that has onchain or billing consequence must be echo-checked against a server declaration, not accepted because it parses.

Fifth, track payment-proof. A consumed-proof store with an atomic single-use claim before resource execution is a piece of infrastructure, not a config flag. If Lightning consolidation lands, having that store already designed is the difference between shipping in a week and shipping in a quarter. The extensions layer is where that work belongs.

Sixth, read the outside PRs. Seventeen of thirty-seven merged changes this week came from people with no obligation to the project. That is the cheapest security research available anywhere, published in full, with reproduction steps. Subscribing to it is a weekly habit, not a project.

Pay per call, settle per call

An OpenAI-compatible gateway where the payment clears before the tokens ship.

Register agent