Skip to content

integration/v0.14.0-rc1#800

Open
bussyjd wants to merge 35 commits into
mainfrom
integration/v0.14.0-rc1
Open

integration/v0.14.0-rc1#800
bussyjd wants to merge 35 commits into
mainfrom
integration/v0.14.0-rc1

Conversation

@bussyjd

@bussyjd bussyjd commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Integration branch for v0.14.0-rc1: everything open ≥ #750, on top of main (= merged rc0).

Included (13 PRs, all merged clean; #751 needed one manual resolution)

PR What
#750 fix(network): reth liveness probe no longer kills consistency recovery
#751 fix(x402): 402 challenge resource.url defaults https on public hosts — conflict resolved: main had landed the equivalent inline fix; kept the shared resolveScheme helper (single source of truth), dropped the PR's redundant hunk
#787 deps: gateway-api → v1.6.1
#789 fix(ui): honor OBOL_NONINTERACTIVE on a real TTY
#790 fix(agentcrd): strip server-managed metadata before ResumeAll
#791 fix(serviceoffercontroller): align .well-known/x402 resource path with openapi
#792 fix(stack): own cluster's ports aren't conflicts under --force
#793 fix(tunnel): idempotent hostname add
#794 deps: cloudflared → 2026.7.2 (tag+digest updated together)
#795 deps: @scalar/api-reference → 1.62.9 (version + SRI updated together)
#796 deps: ethereum EL/CL client bumps
#797 deps: obolup.sh (helmfile 1.7.1, ollama 0.32.1, …)
#798 feat(x402): auth-capture unlock gate + fee revenue metrics (notes: #799)

go build ./... + go test ./... fully green on the tip.

Dependency matrix

Artifact Pin Consistency check
cloudflared 2026.7.2@sha256:4f6655… tag + digest bumped atomically (#794)
gateway-api CRDs v1.6.1 single source (internal/embed/infrastructure/helmfile.yaml)
EL/CL charts reth v2.4.1, v1.39.1, v3.5.2, v7.1.7 bumped together (#796)
scalar bundle 1.62.9 SRI hash updated in same diff (#795)
obolup.sh tooling helmfile 1.7.1, ollama 0.32.1 (#797)
stack-owned images (verifier/controller/buyer/broker) :__OBOL_IMAGE__ placeholders enforced by embed_image_pin_test.go (green) — CI must build+publish component images on the rc1 tag (v0.13.0-rc1 shipped without them; don't repeat)
x402 facilitator (cross-repo) needs x402-facilitator-prometheus-overlay:2.0.2-auth-capture.2+ #798's auth-capture unlock requires it; obol-infrastructure#2986 pins .2 (.1 doesn't register the blueprint). Feature is config-gated off, so rc1 is safe to ship before #2986 — but the unlock can't be enabled until the hosted facilitator serves v2-eip155-auth-capture

Cross-checked against the silvernuc3 production drift (what the field actually needed)

silvernuc3 hand-fix rc1 status
verifier authcap1 (auth-capture unlock + fee metrics) #798
402 https resource URLs behind the tunnel (trustedIPs drift) #751
tunnel hostname re-add failures #793
discovery x402/openapi path mismatch #791
facilitator serving auth-capture (sidecar .2 drift) ➡ cross-repo, obol-infrastructure#2986
facilitator RPC via eRPC (public base RPC 429s on sendRawTransaction) ➡ infra config, not stack code
hermes-config resync wipe (mcp_servers/model drift lost 4×) closed — the MCP/model/max-turns render already landed on main via the rc0 audit line; the remaining #673 delta (full agent model-strip across 402 extra/copy/bazaar/skill.md/services.json/pay-agent + UTF-8 catalog charset) is now cherry-picked into this branch (fix/agent-model-strip-rc1, 6 commits). #673 closed as superseded.
chat widget (burner session, storefront frame) ➡ re-land opened as draft #801 (clean revert of the carve-out; live-validated on mainnet today incl. withdraw) — merge there or into a later RC per the batch-settlement rework decision
paid-for-empty (settle on empty upstream 200) ➡ hermes-side fix (return non-2xx on failed completion), different component

https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v

bussyjd and others added 30 commits July 14, 2026 17:06
The upstream ethereum-node chart's reth subchart defaults to a tcp
liveness probe on p2p (30303) with failureThreshold 3 (~12 min grace).
Reth keeps that port closed while running post-crash check_consistency
recovery, which can take far longer on a large datadir — so the kubelet
SIGKILLs it mid-recovery, each kill adds more drift to heal on the next
start, and the node crash-loops permanently (observed: 637 restarts,
~129k blocks of StoragesHistory drift).

Override the probe with failureThreshold 10000 so it effectively never
fires; a genuinely dead reth exits the container on its own. Scoped to
reth only — geth/nethermind/besu probe http-rpc and erigon probes
metrics, none of which close during recovery.

Claude-Session: https://claude.ai/code/session_01VLQSsnH9WdAsnVYGTd2omr
buildResourceURL keyed the scheme off X-Forwarded-Proto alone, so behind
a TLS-terminating tunnel (edge https -> plaintext to Traefik -> verifier)
any route without an explicit X-Forwarded-Proto:https RequestHeaderModifier
produced http:// resource URLs in 402 challenges. The controller's
host-bound so-<name>-host routes carry that filter but the shared-origin
so-<name> routes do not — and being more path-specific for
/services/<name>, they win the match even on dedicated-hostname origins,
so challenges on those origins advertised http:// resources on an https
endpoint (strict v2 payment clients and discovery crawlers see a
scheme mismatch; observed live on 5 of 8 host-bound offers, #679).

resolveSiteURL already solved exactly this for 402-page links: default
https, downgrade only for hosts the stack serves locally over plain HTTP
(obol.stack, loopback, *.localhost/*.local), explicit signals always win.
Extract that resolution into resolveScheme and use it in both places, so
challenge resource URLs and page links can never disagree again.

Refs #679

Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… openapi for inference/agent

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

https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v
Inline first-message fee capture for gate:auth offers: the SIWX session is
minted by settling an x402 auth-capture charge (EIP-3009 single-shot,
autoCapture) instead of a plain signature; subsequent requests ride the
session cookie free. The escrow enforces the client-signed fee split
(feeBps -> feeRecipient) on-chain at charge() time.

- internal/x402/authcapture.go: auth-capture requirement builder + signed
  payload validation (validateSignedUnlockRequirement pins every economic
  field of the client-signed requirement against config)
- internal/x402/unlockgate.go: unlock flow — 402 (x402Version 2) ->
  verify+settle against the signed payload -> mint SIWX cookie
- internal/x402/metrics.go: obol_x402_verifier_fee_revenue_atomic_total +
  settled_volume_atomic_total (network/asset/fee_recipient), excluded from
  route-pruning
- config: global authCaptureUnlock block (offerPrefix, price, payTo,
  feeRecipient, min/maxFeeBps, captureAuthorizer)

Config-gated, off by default. Settlement must use the client's signed
'accepted' requirement verbatim (PaymentInfo hash commits server-issued
deadlines; rebuilding drifts them and breaks the signature).

Proven end-to-end on Base Sepolia and Base mainnet (on-chain fee split,
cookie mint, free-ride, metrics materialization).

Claude-Session: https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v
An Obol Agent (type=agent) runs its own model, skills, and memory — the buyer
never selects one, and Hermes ignores the chat-completions `model` field
(resolved from its own config). Surfacing the underlying model in the 402 page
is noise and exposes an implementation detail, and pay-agent's required
`--model` flag had no effect on agent calls.

- paymentrequired.go agentCopy: remove '(running <model>)', the '"model":' line
  in the example body, and '--model <id>' from the pay-agent example. The agent
  copy is now model-free.
- buy-x402 buy.py: pay-agent no longer accepts/sends --model (synthesised body
  is just {messages, stream}); usage strings updated.
- buy-x402 SKILL.md: pay-agent documented without --model.

go build + x402/buyer/embed tests green.

Claude-Session: https://claude.ai/code/session_01XgUndZjSoxr2jNNGG5sVYD
(cherry picked from commit e605f55)
mergeAgentExtras no longer adds extra.agentModel — an Obol Agent runs its own
model and the buyer never selects one, so the model id is an internal detail,
not buyer-facing info (it also rendered in the HTML 402 page's raw-JSON card).
agentSkills/agentRuntime still surface so clients can tell it's an agent.

Claude-Session: https://claude.ai/code/session_01XgUndZjSoxr2jNNGG5sVYD
(cherry picked from commit 92716d6)
…t offers

For agent offers the buyer never selects a model — the agent runs its own and
ignores the chat-completions `model` field — so the bazaar discovery example
now seeds the neutral 'your-model-id' placeholder instead of the real upstream
id (which also rendered in the HTML 402 page's embedded raw-JSON card). Inference
offers are unchanged: there the model IS buyer-selectable, so the real id stays.

Claude-Session: https://claude.ai/code/session_01XgUndZjSoxr2jNNGG5sVYD
(cherry picked from commit 0f86017)
…for agent offers

The /skill.md catalog (and its Service Details section) showed the agent's
underlying model in the Model column / **Model** bullet. An agent runs its own
model and ignores the request `model` field, so the id is an internal detail —
agent rows now render '—' and omit the **Model** bullet. Inference offers keep
their model (there the buyer selects it). Mirrors the 402 page/extra/bazaar
model-strip in internal/x402. The /api/services.json feed still resolves the
agent model on purpose (drives the storefront UI) — left untouched.

Claude-Session: https://claude.ai/code/session_01XgUndZjSoxr2jNNGG5sVYD
(cherry picked from commit 2a88c3f)
bussyjd added 2 commits July 20, 2026 19:07
…json for agent offers

The catalog JSON builder set the entry model from spec.model / the resolved
agent model, bypassing catalogModelName -- so /api/services.json still leaked
the internal model id for type=agent offers even though skill.md, the 402
page/extra, and the bazaar example already strip it (#673). Route the JSON
builder through catalogModelName so all discovery surfaces agree: agent offers
omit the model (they run their own and ignore the request `model`), inference
offers keep it. Invert the test that pinned the old leaky behaviour.

Also folds in pre-existing #673 housekeeping that was still uncommitted:
- buy-x402 SKILL.md + buy.py: drop stale --model residue
- agentcrd contract test: fix stale lifetime_seconds 90 -> 180 drift

Claude-Session: https://claude.ai/code/session_01XgUndZjSoxr2jNNGG5sVYD
(cherry picked from commit f8c0d8e)
…mojibake

The skill-catalog busybox httpd served .md/.html with a bare text/* Content-Type
(no charset), so clients fell back to Latin-1/CP1252 and rendered UTF-8 em dashes
(the catalog's '—' placeholders, accented operator descriptions) as '—'. Add
charset=utf-8 to the text MIME mappings. JSON stays clean (always UTF-8 per RFC 8259).

Claude-Session: https://claude.ai/code/session_01XgUndZjSoxr2jNNGG5sVYD
(cherry picked from commit 0229b8f)
bussyjd added 2 commits July 20, 2026 20:24
handlePaidUnlock discarded the settle response when facilitatorSettle
errored, losing the tx hash the facilitator returns when it submits the
settle tx on-chain and then fails on the receipt path. A charged buyer got
a bare settle_failed with no cookie, no answer, and no way to reconcile the
on-chain debit.

Mirror the per-request paid path (HandleProxy): surface settleResp.Transaction
via X-PAYMENT-RESPONSE + a 'you may pay twice' hint, and log it, on both the
transport-error and !Success branches. A failed settle still mints no session.

Claude-Session: https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v
@bussyjd
bussyjd marked this pull request as ready for review July 20, 2026 18:31
@bussyjd

bussyjd commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Ready for merge. Updated to 1eb291e9 — folded in the P1 settle-tx-hash fix (#798's f420d787) so rc1 doesn't ship the pre-fix auth-capture code it already contains.

Merge relationships:

Deployed + verified on silvernuc3: rc1 + all three review fixes running live, auth-capture fee capture proven on Base mainnet (0.5% split on-chain).

https://claude.ai/code/session_01PnhCQLz7CHuDBUhWd5xF8v

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