ObolNetwork / ObolNetwork/obol-stack

feat(sell): x402scan discovery correctness for host-bound offers — companion to #668

Open
#679 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
11
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Summary

#668 proposes binding each offer to its own hostname (one tunnel, N origins) — exactly the right substrate, because x402scan groups discovered resources per origin (scheme://host). This is a companion to #668: the set of discovery-correctness behaviors obol-stack should emit/validate by default so that a host-bound offer actually indexes cleanly on x402scan, rather than being silently skipped.

Grounded in (a) reading x402scan's discovery implementation (Merit-Systems/x402scan + @agentcash/discovery), and (b) field experience running three offers (an ~80-endpoint HTTP API, an OpenAI-compatible agent, and an ERC-8004 feed) behind a single cloudflared tunnel.

Why — x402scan's actual rules (from its source)

  • Per-origin grouping → an offer needs its own hostname (#668). ✔
  • An OpenAPI operation is treated as paid only if it carries x-payment-info ({ price: {mode,currency,amount}, protocols: [{x402:{}}] }). Operations marked security: [] or referencing an apiKey-typed security scheme are classified unprotected / apiKey and skipped (@agentcash/discovery inferAuthMode; SKIP_AUTH_MODES = {unprotected, apiKey}). This is the biggest footgun: the conventional way to model x402 in OpenAPI — an apiKey securityScheme (header X-PAYMENT) — makes every paid endpoint invisible to x402scan.
  • It reads /openapi.json and /.well-known/x402 FREE at the origin root.
  • It probes each endpoint and requires the 402 challenge's resource.url to byte-match the probed URL (scheme included).
  • It indexes base + solana only (testnets are skipped).
  • It does not expand {path} templates — templated routes register as literal %7B…%7D (effectively uncallable); query-param endpoints index cleanly.

Gaps in obol-stack today (what an operator must hand-fix)

  1. No x-payment-info story. A service whose OpenAPI marks paid ops with an apiKey security scheme (the natural modeling) is silently skipped. We had to drop security and emit x-payment-info per op to get listed.
  2. Discovery docs. #668 makes /skill.md, /api/services.json, and ERC-8004 host-aware, but x402scan reads /openapi.json + /.well-known/x402 at the origin root — those must be served free there. Today the operator wires that by hand.
  3. resource.url exactness. The x402-verifier's 402 must carry the public https origin+path. We had to force X-Forwarded-Proto: https and pin the service's PUBLIC_BASE_URL. For a host-bound offer (#668 spec.hostname) the verifier could derive the public origin automatically.
  4. No network lint. A stale/testnet payment.network silently makes an offer un-indexable (cf. #612). obol sell could warn when network ∉ {base, solana}.
  5. Templated path params. Operators get %7B…%7D resources on x402scan unless they use query params — worth a doc note / lint.

Proposal (companion to #668; small, declarative)

  • Verifier: when an offer is host-bound (#668 spec.hostname), emit the 402 resource.url as https://<hostname><path> (honoring X-Forwarded-Proto/X-Forwarded-Host), so probe == challenge holds by construction (closes gap 3 generically).
  • Discovery: serve (or transparently proxy the upstream's) /openapi.json + /.well-known/x402 free at the offer origin root, alongside the #668/#663 host-aware /skill.md + /api/services.json. (Relates to #563 "publish a swagger file on the tunnel" and #596 "llms.txt for tunnel storefronts".)
  • obol sell validation / lint: warn if payment.network ∉ {base, solana}; optionally lint the upstream's /openapi.json for paid ops that are missing x-payment-info or that use security: [] / apiKey schemes (the two skip cases), and flag literal {path} / %7B%7D resources.
  • Docs: an "x402scan discovery contract" operator checklist — per-origin host, x-payment-info (not apiKey security), free root /openapi.json + /.well-known/x402, exact https resource.url, base/solana, query-params over path-templates.
  • DNS UX (relates to #668 step 5): prefer a remotely-managed (token) tunnel so adding a Public Hostname auto-creates its DNS record — removing the manual CNAME step. Today the default is a locally-managed config.yml with hand-created DNS, which is also where the "old catch-all rule shadows new hosts" ordering footgun comes from.

Field context (verified)

Three offers behind one cloudflared (currently locally-managed config.yml, explicit per-host ingress, manually-created DNS). Before addressing the above, x402scan listed 0 of ~80 paid endpoints — all classified apiKey/unprotected and skipped — even though every endpoint correctly returned a 402 challenge. After emitting x-payment-info, dropping the apiKey security scheme, serving free /openapi.json + /.well-known/x402 at the host root, pinning the public https resource.url, and moving path params to query params, all paid endpoints index cleanly, while infra/meta endpoints (/healthz, /openapi.json, MCP plumbing) are intentionally excluded via security: [].

References

  • #668 host-based offer routing (substrate) · #669 storefront surface contract · #663 storefront branding / /api/storefront.json · #671 paid chat via batch settlement · #664 sell --accept CLI bug · #656 / #562 multiple storefronts · #596 llms.txt for tunnel storefronts · #563 publish a swagger file on the tunnel · #612 base-sepolia EIP-712 domain
  • x402scan discovery: Merit-Systems/x402scan (apps/scan/src/lib/url.ts, apps/scan/src/lib/discovery/*, @agentcash/discovery inferAuthMode).

Filed via Claude Code from field experience running multiple x402 offers on obol-stack + reading the x402scan discovery source. https://claude.ai/code/session_01VquWN9UMaSHH7MHGcG8bw1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with #668 and the existing obol sell, verifier, and discovery entry points; compare the stated behavior with x402scan's apps/scan/src/lib/url.ts, apps/scan/src/lib/discovery/*, and @agentcash/discovery inferAuthMode. Done means the proposed host-bound resource URLs, free discovery routes, network and OpenAPI linting, operator checklist, and DNS behavior are defined and validated.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, openapi
Domain
api, backend-api-design, cli, networking
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.