Skip to content

fix(serviceoffercontroller): align well-known/x402 resource path with openapi for inference/agent#791

Merged
OisinKyne merged 1 commit into
mainfrom
fix/discovery-x402-resource-path
Jul 20, 2026
Merged

fix(serviceoffercontroller): align well-known/x402 resource path with openapi for inference/agent#791
OisinKyne merged 1 commit into
mainfrom
fix/discovery-x402-resource-path

Conversation

@bussyjd

@bussyjd bussyjd commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Problem

For hostname-bound inference and agent ServiceOffers (the common case: spec.type empty/inference/agent, no custom spec.routes), the two discovery surfaces advertised different paths for the same paid operation:

  • openapi.json — built by buildOfferScopedOpenAPIopenAPIPathsForOffer, which special-cases inference/agent and always emits a single path /v1/chat/completions, ignoring spec.routes.
  • /.well-known/x402 — built by buildOfferWellKnownX402, which iterates EffectiveRoutes() and maps each route path via openAPIRelPathForRoute. With no declared routes, EffectiveRoutes synthesizes a paid catch-all /*; openAPIRelPathForRoute("/*") collapses to "", so the resource URL became <origin>/ (offer root).

An x402 crawler that cross-references openapi.json with /.well-known/x402 therefore saw <origin>/v1/chat/completions vs <origin>/ and treated them as disagreeing resources. HTTP/fine-tuning offers with an explicit route table already agreed; the bug was only the inference/agent hardcoded-path special-case known to one builder.

Fix

In internal/serviceoffercontroller/offerbundle.go only:

  1. Added openAPIRelPathForOfferRoute(offer, routePath), which mirrors openAPIPathsForOffer's per-type path selection: inference/agent → fixed /v1/chat/completions; every other type → existing openAPIRelPathForRoute(routePath).
  2. buildOfferWellKnownX402 now builds each resource URL with that helper instead of calling openAPIRelPathForRoute directly.

openAPIPathsForOffer, openAPIRelPathForRoute, and buildOfferWellKnownX402FromOpenAPI are unchanged.

Regression pin: TestBuildOfferBundles_InferenceOfferAgreesWithOpenAPI in hostoffer_test.go — hostname-bound inference offer with no Spec.Routes; asserts openapi paths is exactly /v1/chat/completions and x402.json resources[0].resource is https://…/v1/chat/completions.

Diagram

flowchart LR
  subgraph offer [ServiceOffer inference/agent]
    Type["spec.type = inference|agent"]
    Routes["spec.routes empty → EffectiveRoutes synthesizes /*"]
  end

  subgraph builders [Discovery builders]
    OA["openapi.json builder\nopenAPIPathsForOffer"]
    XK["/.well-known/x402 builder\nbuildOfferWellKnownX402"]
  end

  subgraph pathLogic [Shared per-type path logic]
    Helper["openAPIRelPathForOfferRoute\n(or openAPIPathsForOffer equivalent)"]
    Fixed["/v1/chat/completions"]
  end

  Type --> OA
  Type --> XK
  Routes --> XK
  OA --> Helper
  XK --> Helper
  Helper --> Fixed
  Fixed --> OAOut["openapi paths:\n/v1/chat/completions"]
  Fixed --> XKOut["x402 resource:\norigin + /v1/chat/completions"]
  OAOut -.->|agree| XKOut
Loading

Validation

$ gofmt -l internal/serviceoffercontroller/offerbundle.go internal/serviceoffercontroller/hostoffer_test.go
# (no output)

$ go build ./...
# (exit 0)

$ go test ./internal/serviceoffercontroller/... -count=1
ok  	github.com/ObolNetwork/obol-stack/internal/serviceoffercontroller	3.005s

https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v

… openapi for inference/agent

Reported by a teammate during v0.14.0-rc0 field testing.

https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v
@OisinKyne
OisinKyne force-pushed the fix/discovery-x402-resource-path branch from c70634a to 576cac6 Compare July 20, 2026 17:43
@OisinKyne
OisinKyne merged commit 0963775 into main Jul 20, 2026
9 checks passed
@OisinKyne
OisinKyne deleted the fix/discovery-x402-resource-path branch July 20, 2026 17:43
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.

2 participants