feat(0.8.2): x402 buyer trust hardening — mandatory guardrails, cross-payment limits, CAIP-2 interop#243
Merged
Merged
Conversation
…+ offer selector Makes "guardrails-first" actually true (external audit F2/F3/F4). - F2: X402Account.fromPrivateKey no longer defaults the policy — an unbounded wallet must pass the named X402SpendPolicy.unsafeAllowAllForTesting(). - F3: X402SpendPolicy gains allowedAssets (pin token), allowedResourceOrigins (pin endpoint scheme://host[:port]), and maxAuthorizationLifetimeSeconds (the signed validBefore is clamped to it, so a seller's maxTimeoutSeconds can't mint a long-lived authorization). A policy-approved recipient no longer implies any token, any URL, or any duration. - F4: new X402OfferSelector (X402Client(account, selector=…)); default LowestAmount pays the cheapest permitted offer instead of the seller's first, so a seller can't order accepts[] to steer the buyer. FirstAllowed restores the prior behavior explicitly. Breaking (pre-1.0): pass a real policy to fromPrivateKey. 6 new tests (37 x402 tests green). Docs: x402.md + CHANGELOG. Follow-up 0.8.2 slices: session/velocity limits, X402Signer seam, module extraction, x402 v2. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Continues the buyer hardening (audit F5 + the signer half of F6). - F5: X402SessionLimits(maxPayments, maxTotalValue, maxPaymentsPerPayee, cooldownMillis) bounds the aggregate spend across calls — the per-payment policy only bounds one. X402Client(account, sessionLimits=…, spendStore=…) checks before any signature and records settled payments in an X402SpendStore (per-process InMemorySpendStore default; use a durable store in production so a restart can't reset a cumulative cap). Over-limit -> X402PaymentDeniedException. - Signer seam: X402Signer interface + LocalKeySigner default; X402Account .fromSigner(signer, policy) signs through it instead of owning a raw key — enables KMS/HSM/scoped session keys, keeping permanent keys out of heap. fromPrivateKey now wraps a LocalKeySigner. 9 new tests (46 x402 tests green); detekt clean. Docs: x402.md + CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-compatible labeling Audit P0c = "add x402 v2". Verified the v2 spec (header rename + CAIP-2 + x402Version:2), but the field-level v2 PAYMENT-REQUIRED/payload schema isn't in the public launch post or Coinbase docs — and the audit cautioned against reverse-engineering a spec. So ship the verifiable v2 piece, defer the rest: - CAIP-2: X402Account resolves any EVM eip155:<chainId> network on an offer (e.g. eip155:84532), so the buyer can pay a v2 seller. Hermetically tested. - Honest labeling: docs + CHANGELOG describe support as "experimental, x402 v1-compatible (CAIP-2 accepted)", not "x402 v2". Full v2 transport (new headers + v2 payload envelope) deferred until the schema is verified against a live v2 facilitator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Hardens the x402 buyer path in response to an external audit that flagged the "guardrails-first" buyer as having optional guardrails. Three commits, all tickets closed, full suite + build + detekt green.
What changed
#4736 — buyer trust hardening (breaking, pre-1.0)
X402Account.fromPrivateKeyno longer defaults an empty policy; an unbounded wallet must pass the explicitly-namedX402SpendPolicy.unsafeAllowAllForTesting().allowedAssets(pin token),allowedResourceOrigins(pin endpoint origin),maxAuthorizationLifetimeSeconds(clamps signedvalidBeforeso a seller'smaxTimeoutSecondscan't mint a long-lived authorization).X402OfferSelector; defaultLowestAmountpays the cheapest permitted offer so a seller can't orderaccepts[]to steer to the costliest.FirstAllowedrestores prior behavior.#4739 — cross-payment limits + signer seam
X402SessionLimits(maxPayments / maxTotalValue / maxPaymentsPerPayee / cooldownMillis) bounds the aggregate across many calls, recorded viaX402SpendStore(default in-memory; back it durably in prod). Limit-exceeding payment raisesX402PaymentDeniedExceptionbefore any signature.X402Signerseam —X402Account.fromSigner(signer, policy)signs through KMS/HSM/wallet-service/scoped session key, keeping permanent keys out of the app heap.fromPrivateKeynow wrapsLocalKeySigner.#4740 — CAIP-2 network ids
Verification
./gradlew test(all modules) — green./gradlew build(incl. detekt gate) — greenRefs #4526 (epic), #4528. Closes #4736, #4739, #4740.
🤖 Generated with Claude Code