v0.14.0-rc3 - It comes up
Pre-releasev0.14.0-rc3 - It comes up
rc2 shipped the features. rc3 makes them survive contact with a real k3s stack.
Two blockers turned up testing rc2 on a stack that wasn't a developer's laptop, and both of them broke something rc2 had just introduced.
obol stack up failed outright on the k3s backend — Kubernetes has rejected loopback addresses in Endpoints since v1.33, and that is exactly what the resolver handed it. And the storefront branding editor, rc2's headline operator feature, iframed a preview origin that never made it into /etc/hosts, so the editor loaded into nothing while every route behind it reported healthy.
Alongside those, the external buyer-tool prompts introduced in rc2 got a correctness pass from real buyer runs.
If you are on rc2, upgrade — especially on k3s.
Important
Behavior change, narrowed since rc2. Offers of type: http that do not declare methods on a paid route advertise GET in openapi.json, /skill.md and /.well-known/x402 — but only for sub-path paid routes. A root-priced route (/, /*, or an offer with no route table at all) keeps advertising POST, because the offer root serves your landing page on GET and only POST falls through to the payment gate. rc2 advertised GET there too, which pointed buyers at a page that could never return 402; rc3 fixes that. Inference, agent and fine-tuning offers are POST throughout. If a sub-path paid route takes a JSON body, declare methods: [POST] on it explicitly.
Note
Carried over from rc2 — known issue: on streaming (SSE) responses the X-PAYMENT-RESPONSE settlement receipt is delivered as an HTTP trailer rather than a header, a consequence of deferring settlement until the stream completes. Many proxies and HTTP clients do not surface trailers, so streaming buyers may not see a receipt even though settlement succeeded — verify on-chain if you need confirmation. Non-streaming responses are unaffected. Still expected to be revisited before the tagged release.
Warning
This software is early alpha, you could lose what you put in. Please use caution when it comes to non-testnet assets.
Install / Upgrade
# Install this release
OBOL_RELEASE=v0.14.0-rc3 bash <(curl -s https://stack.obol.org)
# Run the stack
obol stack init && obol stack upRelease Highlights
obol stack up no longer fails on k3s
k3s runs directly on the host, so host resolution landed on 127.0.0.1 — and that value was stamped straight into the ollama Endpoints object, which Kubernetes rejects:
UPGRADE FAILED: cannot patch "ollama" with kind Endpoints:
Endpoints "ollama" is invalid: subsets[0].addresses[0].ip:
Invalid value: "127.0.0.1": may not be in the loopback range
The rejection is correct — inside a pod's network namespace 127.0.0.1 is the pod itself, not the host, so that endpoint could never have routed anywhere. Host resolution now substitutes the host's primary routable IPv4 whenever it lands on loopback. The guard sits in the shared resolver rather than a k3s branch, so it also covers Docker runtimes that map host.docker.internal to loopback. obol sell carried a drifted second copy of the same logic with the same bug; it now delegates to the shared resolver.
The branding editor's preview actually loads
The /storefront branding editor iframes storefront-preview.obol.stack, so that name has to resolve locally. obol stack up appended it at one call site — but the managed /etc/hosts block is replaced wholesale, and the agent-resume path runs last and rewrote the block without it. Observed on a real k3s stack: the HTTPRoute and its backend were healthy and answered fine on a Host header, while the editor's iframe had nowhere to load from.
The preview origin is a fixed property of every local stack, exactly like the base domain, so it is now emitted unconditionally instead of depending on which caller writes last.
Root-priced offers are payable as advertised again
rc2 advertised GET <origin>/ as the paid resource for any type: http offer whose paid route was not given an explicit methods list. That is the one method the offer root does not route to the payment gate — the root serves your storefront landing page on GET, and only POST falls through. Buyers following discovery got a 200 HTML page and no way to pay.
The advertised method is now derived from the route's shape: root-priced routes advertise POST, sub-path routes keep rc2's GET default. This affected any paid route declared without methods, including route tables whose catch-all omitted them — not only offers with no route table.
The release smoke could not catch this, because the payment gate matches on path alone and returns 402 regardless of method; a bare GET probe passed while discovery advertised something unpayable. rc3 adds the missing unit assertion: the advertised method must not be one the static site claims first.
A failed upstream probe no longer sticks
The controller caches each offer's upstream openapi.json and builds that offer's /.well-known/x402 and openapi.json from it — one entry per real paid route. It was caching failed probes too, keyed on the offer's generation, so a single miss pinned that offer to a bare route-table fallback until the ServiceOffer was next edited. An offer whose upstream was briefly slow could advertise a single root entry indefinitely.
Failed probes are no longer cached, so the next reconcile retries. Offers that legitimately have no upstream document — agent, inference, or no upstream service — are still probed only once per generation rather than on every reconcile.
Note
This does not remove the brief window after a controller restart, when the cache starts empty and each offer's advertised routes reappear as its own reconcile lands. Measured on a nine-offer stack during this release's validation: documents thin out for roughly 30–90 seconds and then fully recover on their own. What this fixes is the case where that state became permanent. Removing the startup window as well is follow-up work.
External buyer prompts, corrected against real runs
The per-tool prompts added in rc2 lost their chatty framing and gained the checks that buyer runs showed were missing: AgentCash and Poncho prompts now require a transaction receipt before treating a payment as done, and Bankr agent buyers are told to reuse the full accepts[] entry rather than reconstructing one — which is what was producing unsupported_scheme rejections.
Breaking changes / Migration notes
- No new breaking changes in rc3. The two rc2 callouts above still apply if you are upgrading from rc1 or earlier.
obol upgrade/obol stack upin place. If rc2 left your stack half-applied on k3s, rerunobol stack up— the failing patch now succeeds.- Bundled dependency bumps: helmfile
1.7.3, ollama0.32.6.
What's Changed
As with rc1 and rc2, the work landed through the integration branch rather than as individual merges to main:
- fix(stack): two k3s blockers — loopback
Endpoints, and the storefront preview host missing from/etc/hosts(#808) - fix(discovery): root-priced
type: httpoffers advertisePOST, notGET, so the advertised paid operation reaches the payment gate (#806) - fix(discovery): never pin a failed upstream OpenAPI probe, so a transient miss cannot collapse an offer's advertised routes (#810)
- fix(buyprompts): shorten external-buyer prompts and harden payment checks (#806)
- deps: bundled renovate updates to
obolup.sh
This RC was validated before release: both k3s fixes confirmed on real hosts, a full 22-flow release smoke, and six on-chain settlement/registration receipts on Base Sepolia — each verified independently against the public RPC.
This RC carries everything from v0.14.0-rc2 — the aggregate /.well-known/x402, external buyer-tool compatibility, deferred SSE settlement, secure branding previews and the CLI version in the footer — and from v0.14.0-rc1 before it.
Full Changelog: v0.14.0-rc2...v0.14.0-rc3
