fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne
, '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(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy - #708

Closed
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening
Closed

fix(infra): PDBs for payment/RPC gates + remote-signer Recreate strategy#708
bussyjd wants to merge 1 commit into
fix/litellm-zero-downtimefrom
fix/replica-stance-hardening

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Replica-stance hardening: PDBs for payment/RPC gates + remote-signer Recreate

Follow-up to #707, from the replica-stance review of the remaining services. Targets multi-node clusters, where drains and RWO volume attach are real.

Stacked on #707 (fix/litellm-zero-downtime) — merge that first; this PR's own diff is the last commit.

Changes

PodDisruptionBudgets (minAvailable: 1)

  • x402-verifier (base/templates/x402.yaml) — the ForwardAuth gate for every paid route. On multi-node, a kubectl drain would silently evict the only pod and 5xx all /services/* traffic; the PDB makes that require explicit operator intent (delete the pod), same stance as the existing litellm PDB.
  • eRPC (base/templates/erpc.yaml) — the RPC front door for agents and the frontend. Lives in the base chart because the ethereum/erpc chart (0.0.4) has no PDB template; its podDisruptionBudget value in our values file is unwired. Selector pinned to the chart's live selectorLabels.
  • cloudflared — already ships a PDB (gated on active tunnel + >1 replica); now pinned by a test so it doesn't regress.

Rollouts are unaffected in all cases: at 1 replica the default RollingUpdate rounds to maxUnavailable 0 / maxSurge 1, and both verifier (/readyz gates on config + routes loaded) and eRPC surge gaplessly behind readiness probes.

remote-signer → strategy: Recreate

The obol/remote-signer chart (0.3.3) cannot express spec.strategy, so signer deployments defaulted to RollingUpdate — a singleton over a ReadWriteOnce keystore PVC. On multi-node, a surge pod can land on another node and wedge forever on the volume attach; on any cluster it briefly double-runs the signer over the same keystore. Same doctrine as hermes and x402-buyer: RWO-backed singletons run Recreate.

Implemented as a post-helmfile-sync kubectl patch (agentruntime.EnforceRemoteSignerRecreate) from both the hermes and openclaw sync paths, mirroring the existing hermes strategy-migration pattern — including the load-bearing explicit rollingUpdate: null that clears the k8s-defaulted block the API would otherwise reject the type flip over. Patching spec.strategy doesn't touch the pod template, so the pin never rolls the signer itself. Helm leaves it alone on later upgrades because the chart never renders the field.

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

Reviewed and deliberately unchanged

  • serviceoffer-controller — flagged in the review as "RollingUpdate without leader election", but the controller already has Lease-based leader election (cmd/serviceoffer-controller/main.go:84, on by default), so its surge is safe: the new pod blocks on the Lease until the old leader releases it. No change; the x402.yaml comment already documents this.
  • Frontend readiness probe (cosmetic local-UI 502s during rollouts) — left for a UI-side change in obol-stack-front-end/chart.

Tests

  • internal/embed/embed_pdb_test.go — table-driven pins for both PDBs (namespace, minAvailable, exact selector — extra labels would silently match no pods) + cloudflared PDB template gating.
  • internal/agentruntime/signer_test.go — patch-args shape incl. the explicit rollingUpdate: null.

Live validation (multi-service k3d cluster from the #707 run)

  • obol stack up created both PDBs: erpc/erpc and x402/x402-verifier, MIN AVAILABLE 1, ALLOWED DISRUPTIONS 0.
  • The stack-up agent re-sync flipped remote-signer from RollingUpdate to {"type":"Recreate"}without rolling the pod (pod age preserved), confirming the patch is non-disruptive.
  • helm lint clean; full go test green on touched packages.

https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD

Replica-stance hardening for multi-node clusters, follow-up to the
LiteLLM zero-downtime work (#321):
- PodDisruptionBudgets (minAvailable: 1) for x402-verifier and eRPC —
on multi-node clusters a kubectl drain would otherwise silently evict
the only pod of the payment gate (every /services/* route 5xxs) or
the RPC front door. Same stance as the existing litellm PDB; rollouts
are unaffected (both surge gaplessly behind readiness probes). The
eRPC PDB lives in base/templates/erpc.yaml because the ethereum/erpc
chart (0.0.4) has no PDB template — its podDisruptionBudget value is
unwired. cloudflared already ships a PDB gated on an active tunnel.
- remote-signer pinned to strategy: Recreate post-helmfile-sync (hermes
and openclaw runtimes). The obol/remote-signer chart (0.3.3) cannot
express spec.strategy, so the deployment defaulted to RollingUpdate —
a surge pod over the RWO keystore PVC wedges on multi-node volume
attach and briefly double-runs the signer anywhere. Patching
spec.strategy does not touch the pod template, so the pin never rolls
the pod itself. Upstream follow-up: add strategy support to the chart.
Reviewed and deliberately unchanged: serviceoffer-controller already
has Lease-based leader election (cmd/serviceoffer-controller/main.go),
so its RollingUpdate surge is safe — the new pod blocks on the Lease.
Claude-Session: https://claude.ai/code/session_01YLuXwN1A4tG3xmEMKvAzeD
@OisinKyne

OisinKyne commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Upstream follow-up: add strategy/updateStrategy support to the remote-signer chart in ObolNetwork/helm-charts, then delete this patch.

We control this chart, lets just fix this there rather than a post-install hook? I assume that would shorten install time

@bussyjd

Copy link
Copy Markdown
ContributorAuthor

Closing as superseded by integration/v0.13.0-rc0 @ a29c826 (rolled up in #716 / release v0.13.0-rc0). All commits of this PR are contained in the RC tip (verified by ancestry/patch-id audit).

@bussyjdbussyjd closed this Jul 8, 2026
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.

2 participants

@bussyjd@OisinKyne