Skip to content

fix(x402-buyer): set obol-buy-x402/1.0 User-Agent on outbound HTTP#503

Merged
bussyjd merged 1 commit into
mainfrom
fix/x402-buyer-user-agent
May 19, 2026
Merged

fix(x402-buyer): set obol-buy-x402/1.0 User-Agent on outbound HTTP#503
bussyjd merged 1 commit into
mainfrom
fix/x402-buyer-user-agent

Conversation

@bussyjd
Copy link
Copy Markdown
Collaborator

@bussyjd bussyjd commented May 18, 2026

Summary

  • Sets User-Agent: obol-buy-x402/1.0 (+https://github.com/ObolNetwork/obol-stack) on every outbound HTTP request the x402-buyer sidecar makes to upstream sellers (both the initial 402 probe and the paid retry carrying X-PAYMENT), mirroring the pattern and exact UA string from c2dddc1.
  • Adds OBOL_BUYER_USER_AGENT env override for sellers that require a different UA shape, matching the buy.py mechanism.

Why

Cloudflare WAF 1010 release-blocker for sellers like v1337. Live evidence from today's demo against https://inference.v1337.org/services/aeon:

# direct call from inside the litellm pod → buyer sidecar at 127.0.0.1:8402/v1/chat/completions
HTTP 403
error code: 1010

Cloudflare blocks the Go stdlib default UA (Go-http-client/1.1) with error code 1010. c2dddc1 fixed this in buy.py (the agent-side prober/signer), but missed the Go sidecar — so probe + sign + register all passed, but the actual paid chat-completions request failed.

Files touched

  • internal/x402/buyer/proxy.gouserAgent var + two Header.Set calls in replayableX402Transport.RoundTrip
  • internal/x402/buyer/proxy_test.go — two new tests asserting UA on probe and paid request paths

Test plan

  • TestProxy_UserAgentOnProbeRequest — httptest server captures probe UA, asserts exact string
  • TestProxy_UserAgentOnPaidRequest — httptest server captures both probe and paid UA, asserts both
  • go test ./internal/x402/buyer/ -count=1 — all tests pass (no regressions)
  • go build ./... — clean build
  • Manual: a follow-up demo run against https://inference.v1337.org/services/aeon should succeed end-to-end after this lands

Risks

None — UA-only change. No payment logic, routing, or auth handling modified.

Sellers behind Cloudflare WAF block the Go stdlib default UA
("Go-http-client/1.1") with HTTP 403 + error code 1010. Live evidence
from today's demo against https://inference.v1337.org/services/aeon:
a direct call from inside the litellm pod to the buyer sidecar at
127.0.0.1:8402/v1/chat/completions returned HTTP 403 error code 1010.

c2dddc1 fixed this in buy.py (the agent-side prober/signer) but missed
the x402-buyer sidecar — the in-pod Go relay that makes the actual paid
chat-completions calls to upstream sellers. The buy flow could complete
probe + sign + register (all via buy.py with the good UA), but the paid
request itself failed because the sidecar sent the default Go UA.

Set userAgent = "obol-buy-x402/1.0 (+https://github.com/ObolNetwork/obol-stack)"
(matching the constant from c2dddc1 byte-for-byte) on both:
- firstReq: the initial unpaid probe that triggers the 402 challenge
- retryReq: the paid retry carrying the X-PAYMENT header

Override with OBOL_BUYER_USER_AGENT env var for sellers that require a
different UA shape (mirrors the buy.py override mechanism).

Add two unit tests (TestProxy_UserAgentOnProbeRequest and
TestProxy_UserAgentOnPaidRequest) using httptest.Server to assert the
UA is present on both the probe and the paid request paths.
@bussyjd bussyjd merged commit 7fd7d49 into main May 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant