fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: harden PVC permissions and paid smoke flows - #615

Merged
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening
Jun 9, 2026
Merged

fix: harden PVC permissions and paid smoke flows#615
bussyjd merged 3 commits into
research/hermes-configmap-skills-deliveryfrom
research/pv-permission-smoke-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Summary

  • replace host-side chown/chmod init handling with UID/GID 1000 runtime ownership for Hermes and spawned agents
  • keep LiteLLM/x402 buyer routing in sync when model ConfigMaps change so paid routes roll correctly
  • harden release smoke flows around qwen36-deep, post-stack Bob wallet seeding, PurchaseRequest readiness, and paid inference settlement checks
  • update the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces

Validation

  • bash -n flows/*.sh
  • git diff --check
  • go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
  • full release smoke passed with OBOL_LLM_MODEL=qwen36-deep, including USDC, live OBOL, and fork OBOL paid inference flows

Notes

This is stacked on #610's branch so reviewers can inspect only the follow-up fixes here.

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Summary

What changed:

  • Replaced host-side chown/chmod init handling with UID/GID/fsGroup 1000 runtime ownership for Hermes and spawned agent workloads.
  • Ensured LiteLLM rolls when model or buyer ConfigMaps change, so paid routes are refreshed when the config changes without an env var change.
  • Hardened paid smoke flows around qwen36-deep, inference ServiceOffers, PurchaseRequest auth counts, LiteLLM readiness, and exact settlement deltas.
  • Moved Bob wallet setup in dual-stack flows to post-stack obol wallet import, then asserts the signer matches deterministic Bob.
  • Updated the Obol stack dev runbook to keep QA CLI-first and avoid custom shell surfaces.

Why it matters:

  • Solves the PVC permission problem without making config read-only and without host-side ownership mutation.
  • Reduces fragile permission scaffolding while keeping runtime writes available.
  • Keeps release-smoke aligned with the supported obol CLI, stack, and obol kubectl surfaces.

Risk level: medium

Commit under test: 1f3088b

Base branch: research/hermes-configmap-skills-delivery

Scope

  • Code
  • Charts / manifests
  • Flows / QA scripts
  • Docs / skills
  • Images / dependencies
  • Other:

Validation

CI checks:

CheckStatusLink
CInot checked in this comment

Unit tests:

bash -n flows/*.sh
git diff --check
go test ./cmd/obol ./internal/buy ./internal/hermes ./internal/serviceoffercontroller ./internal/embed ./internal/stack -count=1
Result: PASS
Commit/SHA: 1f3088b

Integration tests:

No separate integration suite was run outside the release smoke.

Flow tests:

FlowNetworkQA machine labelWorktreeResultArtifacts
flow-01-prerequisiteslocalremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-02-stack-init-uplocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-03-inferencelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-04-agentlocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-05-networkBase Sepolia RPCremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-06-sell-setupBase Sepolia x402 pricingremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-07-sell-verifyx402 tunnel routeremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-10-anvil-facilitatorBase Sepolia Anvil forkremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-08-buyBase Sepolia Anvil fork / USDCremote ARM64 QA hostredactedPASSreceipt retained on QA host
flow-09-lifecyclelocal k3dremote ARM64 QA hostredactedPASSrelease-smoke report retained on QA host
flow-11-dual-stackBase Sepolia / USDCremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-14-live-obol-base-sepolialive Base Sepolia / OBOLremote ARM64 QA hostredactedSKIP only for optional registration receiptreceipt summary retained on QA host
flow-13-dual-stack-obolBase Sepolia Anvil fork / OBOLremote ARM64 QA hostredactedPASSreceipt summary retained on QA host

Release smoke:

Command: flows/release-smoke.sh
Flags/env: OBOL_DEVELOPMENT=true, OBOL_NONINTERACTIVE=true, OBOL_FORCE_REBUILD_LOCAL_DEV_IMAGES=serviceoffer-controller, OBOL_LLM_MODEL=qwen36-deep, RELEASE_SMOKE_INCLUDE_OBOL=true, RELEASE_SMOKE_INCLUDE_OBOL_FORK=true
Endpoint/RPC/worktree details: redacted
Result: PASS

Live Chain Evidence

Do not include private keys, seed phrases, passwords, hostnames, personal paths, or raw bearer tokens.

Network:

  • Base Sepolia
  • Base Sepolia Anvil fork

RPC/provider:

  • Paid Base Sepolia RPC, redacted
  • Local Anvil fork for forked flows

Facilitator:

  • Public Obol facilitator for live Base Sepolia flows, hostname omitted
  • Local x402-rs facilitator for fork flow

Contracts and tokens:

NameAddressVersion / notes
Base Sepolia OBOL0x0a09371a8b011d5110656ceBCc70603e53FD2c78live token
Base Sepolia USDC0x036CbD53842c5426634e7929541eC2318f3dCF7elive/fork token
ERC-8004 identity registry0x8004A818BFB912233c491871b3d84c89A494BD9eBase Sepolia
Fork OBOL0x210BBd033630e5e611B7922D70b0Caabe64636d9flow-13 deployed token

Wallet roles:

RoleAddressSource
Alice / seller / register0xC0De030F6C37f490594F93fB99e2756703c4297Eruntime-derived from configured signer
Bob / buyer / payer0x57b0eF875DeB5A37301F1640E469a2129Da9490Edeterministic second-derived buyer
Single-stack flow-08 seller0x70997970C51812dc3A010C7d01b50e0d17dc79C8local flow seller

Balances:

TokenAddressBeforeAfterExpected deltaActual delta
Live OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E3000000000000000031000000000000000+1000000000000000+1000000000000000
Live OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E49470000000000000004946000000000000000-1000000000000000-1000000000000000
Fork OBOL, Alice0xC0De030F6C37f490594F93fB99e2756703c4297E1000000000000000000010001000000000000000+1000000000000000+1000000000000000
Fork OBOL, Bob0x57b0eF875DeB5A37301F1640E469a2129Da9490E100000000000000000009999000000000000000-1000000000000000-1000000000000000

Transaction receipts:

PurposeTx hashFromToAmount / eventStatus
ERC-8004 registration0x8bc7bf9c5daaaa3c3df889ce7ee44f483593e0b79fd9126a34838314280e1310AliceregistryAgent ID reflected in ServiceOfferPASS
Metadata / service offerMetadata tx unavailable, ServiceOffer status reflected Agent IDSKIP
Live OBOL approval / permit0x51841c0cfcfd1b5e0b23d2976a755f8ed7d11489015e885a962bf3b1c7cd878cBobPermit2Max allowance confirmedPASS
Live OBOL settlement transfer0xb78fa7b9f1097fb88fd6e5fb6d04ffbfc03337f478691b0905eb4d70f0dc2981BobAlice1000000000000000 wei OBOLPASS
Fork OBOL buyer funding0x733a277dd44da427bd3749b955ab4ab77fe9dadbe94358c130afab2ccd4600dedeployerBob10 OBOL mintedPASS
Fork OBOL approval / permit0x40dc8bc61cb2f4dbd31d532141ef1885f3efb29144ee2fc50f712dd265ffa3c8BobPermit2Max allowance confirmedPASS
Fork OBOL settlement transfer0xb0d8974636102f1be97383c4fb1ba8aa860ef7850084ce5d425e37c1016b114dBobAlice1000000000000000 wei OBOLPASS
Flow-08 USDC settlement transfer0x1e7fccd95dfd47e2720896406108f94d391d4f8d5824c37f9d3cdfdc1ca7f9c2buyerseller1000 micro-USDCPASS
Flow-11 USDC settlement transfer0x6127dbec3139eadc68b5343a2ccf552fcf82595c4cdeaf81c893d51ee74754dfBobAlice1000 micro-USDCPASS

Runtime Evidence

QA environment:

ItemValue
OS / archLinux / arm64 remote QA host
Backendk3d
Tool versionsbranch-built obol CLI, Foundry tools available
QA agent/modelqwen36-deep via redacted OpenAI-compatible endpoint

Images:

ComponentImageTag / digestSource
serviceoffer-controllerlocal dev imagerebuilt during smokebranch source
x402 facilitatorghcr.io/obolnetwork/x402-facilitator-prometheus-overlay1.4.9flow-13 preflight

Kubernetes / stack:

ItemValue
Stack IDsomitted to avoid host-local details
Namespacesllm, x402, erpc, hermes-obol-agent
Pod readinessall smoke-gated pods Ready during flow checks
Cleanup resultno k3d clusters remaining after final smoke

Model and routing:

ItemValue
Agent/model usedqwen36-deep
LiteLLM routepaid/qwen36-deep
Paid endpoint statusHTTP 200 with non-empty response
Auth token sourcePurchaseRequest plus x402-buyer sidecar, no raw token logged

Artifacts and logs:

ArtifactLocation / linkNotes
Release smoke reportretained on QA host, path omittedresult PASS
Flow-11 receiptsretained on QA host, path omittedUSDC settlement
Flow-14 receiptsretained on QA host, path omittedlive OBOL settlement
Flow-13 receiptsretained on QA host, path omittedfork OBOL settlement

Demo readiness:

ItemStatusNotes
Seller visible / registeredPASSServiceOffer Ready; Agent ID reflected where registration was enabled
Buyer discovery worksPASSBuyer discovered service catalog / registry data
Paid route worksPASSpaid/qwen36-deep returned HTTP 200
Settlement visible on-chainPASSTransfer receipts and exact deltas verified

Review Notes

Known gaps:

  • flow-11 and flow-14 recorded one SKIP each for unavailable registration receipt while ServiceOffer status already reflected the Agent ID.
  • PR is stacked on research/hermes-configmap-skills-delivery, so reviewers should compare against that branch for the focused delta.

Follow-ups:

  • None required for the validated smoke path.

Reviewer focus:

  • UID/GID/fsGroup 1000 ownership model versus the removed host-side chown/chmod path.
  • LiteLLM ConfigMap reload behavior for paid routes.
  • Flow-08 retry/readiness behavior around obol buy inference and LiteLLM rollout.
  • Post-stack Bob wallet import in dual-stack flows.

bussyjd added a commit that referenced this pull request Jun 9, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
…with #614
Applies the verified findings from the cross-review against PR #614 (every
item adversarially confirmed against the refs; union merge-tree clean).
PVC / upgrade path:
- llm.yaml: restore container-level runAsUser/runAsGroup 1000 on x402-buyer.
Clusters upgraded in place from <= rc12 keep hostPath-typed PVs where
kubelet skips fsGroup; their /state dir is 1000:1000 with consumed.json
written 0600 by UID 1000 — a 65532 sidecar cannot read it, Fatalf's on
`load state`, and takes every paid/<model> route down. On fresh local-type
PVs the explicit UID is harmless (fsGroup 65532 grants group access).
embed_buyer_state_test.go updated to pin the new contract.
- plans/volume-permission-hardening.md: new "Upgrading from <= v0.10.0-rc12"
section — supported path is cluster recreation (wallet backup/restore),
with a documented k3d chown escape hatch. troubleshooting.md gets the
symptom->fix entry. The Hermes half of the legacy-PV breakage cannot be
patched at runtime without reintroducing the chown machinery this PR
removes, so it is a documented breaking change instead.
Paid-route availability:
- llm.yaml: Reloader annotation narrowed to litellm-config only. The buyer
ConfigMaps (x402-buyer-config/x402-buyer-auths) are rewritten by the
controller on every buy, top-up, auto-refill, and tombstone cleanup;
with strategy Recreate + 1 replica the previous annotation bounced the
entire inference gateway (all Hermes traffic, in-flight SSE streams) on
every purchase event, inverting CLAUDE.md pitfall 7 (restart is fallback,
not the default buy path). The buyer hot-reloads via /admin/reload.
stack_test.go updated to pin litellm-config-only.
Flow alignment with #614:
- lib.sh: `stack down` -> `stack down --yes` in reset_flow_workspace. #614's
flow-16 (now last in the single-stack array) intentionally leaves a live
agent offer; without --yes the non-TTY ConfirmRunningServicesLoss gate
refuses, graceful down is silently skipped (`|| true`), and teardown
degrades to the raw k3d-delete fallback on every release-smoke run.
- flow-11: post-register Ready poll 120s -> 300s to match flow-14's identical
live-Base-Sepolia chain-watch path (pitfall 13 free-tier RPC throttling).
Known follow-ups (not in this commit): flow-08 buy-retry top-up vs exactly-N
assertions on rare partial failures; flow-11 lacks flow-14's remote-signer
rolled guard; aztec PVC has no permission story (runs as root today);
post-merge controller repin so released sub-agents pick up this PR's
UID-1000 render (tracked in #614's pin-test note).
Records the options ladder for 'nodes joining my cluster' (far-future, not
scheduled) so near-term permission decisions don't foreclose it: the
host-FS-canonical coupling and its four conditions, the agent-homes-only
scope, what breaks on day one of a join (WaitForFirstConsumer + no
nodeSelector in any render), options 0-4 (home-node pattern -> NFS ->
distributed storage -> API-mediated access -> hybrid by data class), join
mechanics (native k3s server + remote agents; macOS stays k3d), and the
decisions that bind today: group-1000 sharing over render-time UID
matching, home-node pinning as a join prerequisite, inputs to API objects
per Remaining Debt.
@bussyjd
bussyjd requested a review from OisinKyneJune 9, 2026 22:58
@bussyjd
bussyjd merged commit 888515b into research/hermes-configmap-skills-deliveryJun 9, 2026
OisinKyne pushed a commit that referenced this pull request Jun 10, 2026
Removed plans whose work landed and whose durable learnings already live in
the skill references:
- obol-sell-demo.md — implemented (`obol sell demo` ships in the CLI)
- post-490-integration-20260513.md — integration landed in May
- release-smoke-hardening-20260513.md — session retro; learnings folded into
obol-stack-dev/references/release-smoke-debugging.md
- inference-v1337-{buy-report,followup}-20260514.md — QA reports; the WAF/UA
findings live in release-smoke-debugging.md §10 (c2dddc1)
Kept: sell-agent-perf.md (in progress), openapi-402-followups.md (pending
walk-through), openapi-redoc-storefront.md (phase 2 deferred),
storefront-buy-inference-cta-handoff.md (active frontend handoff),
volume-permission-hardening.md (PR #615 owns its rewrite).
@OisinKyne
OisinKyne deleted the research/pv-permission-smoke-hardening branch July 1, 2026 12:33
Sign up for freeto 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

@bussyjd