From 7af6ca6ce6cedc2bd00f81889f1f7934459e4f50 Mon Sep 17 00:00:00 2001 From: bussyjd Date: Wed, 27 May 2026 14:15:49 +0400 Subject: [PATCH] fix(stack): refresh x402 runtime image pins --- internal/embed/embed_image_pin_test.go | 28 +++++++++++++++++++ .../infrastructure/base/templates/llm.yaml | 4 +-- .../infrastructure/base/templates/x402.yaml | 2 +- internal/hermes/hermes.go | 2 +- internal/hermes/hermes_test.go | 1 + .../serviceoffercontroller/agent_render.go | 2 +- .../agent_render_test.go | 1 + 7 files changed, 35 insertions(+), 5 deletions(-) diff --git a/internal/embed/embed_image_pin_test.go b/internal/embed/embed_image_pin_test.go index 051b8c0a..92ff5ec1 100644 --- a/internal/embed/embed_image_pin_test.go +++ b/internal/embed/embed_image_pin_test.go @@ -224,6 +224,34 @@ func TestEmbeddedImages_NamedImagesAreDigestPinned(t *testing.T) { } } +func TestEmbeddedImages_X402ControllerAndBuyerUseFixPins(t *testing.T) { + cases := []struct { + file string + ref string + }{ + { + file: "base/templates/x402.yaml", + ref: "ghcr.io/obolnetwork/serviceoffer-controller:f5d94fc@sha256:c6aa6259e3a6bc61a5f4f7203d8c68cfdd861a8d365f9629d234d13b949bf48e", + }, + { + file: "base/templates/llm.yaml", + ref: "ghcr.io/obolnetwork/x402-buyer:f5d94fc@sha256:0c431eda44e9e2fe5dd50c82cf4885f9be5037e592478781c51e9c510171265c", + }, + } + + for _, tc := range cases { + t.Run(tc.ref, func(t *testing.T) { + data, err := ReadInfrastructureFile(tc.file) + if err != nil { + t.Fatalf("read %s: %v", tc.file, err) + } + if !strings.Contains(string(data), "image: "+tc.ref) { + t.Fatalf("%s must pin current x402 bundle image %q", tc.file, tc.ref) + } + }) + } +} + // TestEmbeddedImages_CloudflaredHelmTagIsDigestPinned covers the cloudflared // chart, which uses the Helm idiom `image.repository` + `image.tag` rather // than a literal `image:` line. The chart template renders diff --git a/internal/embed/infrastructure/base/templates/llm.yaml b/internal/embed/infrastructure/base/templates/llm.yaml index ae7d89c2..3cf98f68 100644 --- a/internal/embed/infrastructure/base/templates/llm.yaml +++ b/internal/embed/infrastructure/base/templates/llm.yaml @@ -293,14 +293,14 @@ spec: - name: x402-buyer # Pinned by sha256 digest (multi-arch manifest list, amd64+arm64) # so the deployed sidecar is byte-for-byte identical across QA - # hosts. The :b13254e tag is preserved for human readability; the + # hosts. The :f5d94fc tag is preserved for human readability; the # digest is authoritative. # Previous tag-only pin allowed the local-build path to silently # reuse a 5-day-old `:latest` image and ate the release-smoke 503 # investigation: stale buyer serialized X-PAYMENT with empty # authorization fields → facilitator /verify 400 → 503 cascade # across flow-08/11/14/13. See internal/embed/embed_image_pin_test.go. - image: ghcr.io/obolnetwork/x402-buyer:b13254e@sha256:446d730fefbe1860e8b3245289aa8979d765ae977b7f0eaa053543e2468313cb + image: ghcr.io/obolnetwork/x402-buyer:f5d94fc@sha256:0c431eda44e9e2fe5dd50c82cf4885f9be5037e592478781c51e9c510171265c imagePullPolicy: IfNotPresent # PSS Restricted: Go distroless:nonroot image already runs as # UID 65532; only the state dir under /state needs to be writeable diff --git a/internal/embed/infrastructure/base/templates/x402.yaml b/internal/embed/infrastructure/base/templates/x402.yaml index f6cdd59d..7f2d1f2a 100644 --- a/internal/embed/infrastructure/base/templates/x402.yaml +++ b/internal/embed/infrastructure/base/templates/x402.yaml @@ -327,7 +327,7 @@ spec: type: RuntimeDefault containers: - name: controller - image: ghcr.io/obolnetwork/serviceoffer-controller:b13254e@sha256:f83bd7e55bdc5d87edb49c04e7fd9257097364e2d43e769c19dfd7c8b47d07af + image: ghcr.io/obolnetwork/serviceoffer-controller:f5d94fc@sha256:c6aa6259e3a6bc61a5f4f7203d8c68cfdd861a8d365f9629d234d13b949bf48e imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: false diff --git a/internal/hermes/hermes.go b/internal/hermes/hermes.go index 041f9a9b..a47aa53a 100644 --- a/internal/hermes/hermes.go +++ b/internal/hermes/hermes.go @@ -781,7 +781,7 @@ func generateValues(namespace, hostname, dashboardHostname, agentBaseURL, token, - sh - -ec - | - mkdir -p /data/.hermes/home /data/.hermes/workspace + mkdir -p /data/.hermes/home /data/.hermes/workspace /data/.hermes/logs if [ ! -x /opt/hermes/.venv/bin/hermes ]; then echo "Hermes binary missing from image: /opt/hermes/.venv/bin/hermes" >&2 exit 1 diff --git a/internal/hermes/hermes_test.go b/internal/hermes/hermes_test.go index 2fa09d1a..8293920c 100644 --- a/internal/hermes/hermes_test.go +++ b/internal/hermes/hermes_test.go @@ -140,6 +140,7 @@ func TestGenerateValues_UsesHermesNativeNames(t *testing.T) { `value: "hermes-obol-agent"`, "OBOL_SKILLS_DIR", "/data/.hermes/obol-skills", + "/data/.hermes/logs", "containerPort: 8642", "containerPort: 9119", "fsGroupChangePolicy: OnRootMismatch", diff --git a/internal/serviceoffercontroller/agent_render.go b/internal/serviceoffercontroller/agent_render.go index e44874cc..c5e9a25f 100644 --- a/internal/serviceoffercontroller/agent_render.go +++ b/internal/serviceoffercontroller/agent_render.go @@ -222,7 +222,7 @@ func buildAgentProfileInitContainer() map[string]any { "image": hermesImage(), "imagePullPolicy": "IfNotPresent", "command": []any{"/bin/sh", "-ceu"}, - "args": []any{`mkdir -p /data/.hermes/home /data/.hermes/workspace /data/.hermes/obol-skills + "args": []any{`mkdir -p /data/.hermes/home /data/.hermes/workspace /data/.hermes/logs /data/.hermes/obol-skills seed=/profile-seed/profile.tar.gz marker=/data/.hermes/.obol-profile-seed-imported diff --git a/internal/serviceoffercontroller/agent_render_test.go b/internal/serviceoffercontroller/agent_render_test.go index e04e1e32..19c43443 100644 --- a/internal/serviceoffercontroller/agent_render_test.go +++ b/internal/serviceoffercontroller/agent_render_test.go @@ -213,6 +213,7 @@ func TestAgentManifests_ProfileSeedInitContainer(t *testing.T) { "/profile-seed/profile.tar.gz", ".obol-profile-seed-imported", "/data/.hermes/SOUL.md", + "/data/.hermes/logs", "cp -R", } { if !strings.Contains(script, must) {