diff --git a/internal/hermes/hermes.go b/internal/hermes/hermes.go index 84c088e0..d92142c9 100644 --- a/internal/hermes/hermes.go +++ b/internal/hermes/hermes.go @@ -935,6 +935,13 @@ func generateValues(namespace, hostname, dashboardHostname, agentBaseURL, token, env: - name: HERMES_HOME value: /data/.hermes + # v2026.7.x images bake HERMES_WRITE_SAFE_ROOT=/opt/data (their + # default HERMES_HOME). We relocate HERMES_HOME to the PVC, so + # the safe root must follow or every file-tool write is denied + # as a "protected system/credential file". /tmp allows scratch + # scripts; the static credential denylist still applies inside. + - name: HERMES_WRITE_SAFE_ROOT + value: /data/.hermes:/tmp - name: HOME value: /data/.hermes/home - name: API_SERVER_ENABLED diff --git a/internal/hermes/hermes_test.go b/internal/hermes/hermes_test.go index 46fc04b2..25a98b37 100644 --- a/internal/hermes/hermes_test.go +++ b/internal/hermes/hermes_test.go @@ -256,6 +256,11 @@ func TestGenerateValues_UsesHermesNativeNames(t *testing.T) { "name: GATEWAY_HEALTH_URL", "HERMES_DASHBOARD_BASIC_AUTH_USERNAME", "HERMES_DASHBOARD_BASIC_AUTH_PASSWORD", + // Must track HERMES_HOME: v2026.7.x images bake + // HERMES_WRITE_SAFE_ROOT=/opt/data and deny all file-tool + // writes outside the safe root. + "name: HERMES_WRITE_SAFE_ROOT", + "value: /data/.hermes:/tmp", } { if !strings.Contains(values, needle) { t.Fatalf("generateValues() missing %q:\n%s", needle, values) diff --git a/internal/serviceoffercontroller/agent_render.go b/internal/serviceoffercontroller/agent_render.go index 4e030929..b7f018f6 100644 --- a/internal/serviceoffercontroller/agent_render.go +++ b/internal/serviceoffercontroller/agent_render.go @@ -326,6 +326,10 @@ fi func agentPodSpec(agent *monetizeapi.Agent) map[string]any { containerEnv := []any{ map[string]any{"name": "HERMES_HOME", "value": "/data/.hermes"}, + // v2026.7.x images bake HERMES_WRITE_SAFE_ROOT=/opt/data (their default + // HERMES_HOME); with HERMES_HOME relocated to the PVC the safe root must + // follow or every file-tool write is denied. + map[string]any{"name": "HERMES_WRITE_SAFE_ROOT", "value": "/data/.hermes:/tmp"}, map[string]any{"name": "HOME", "value": "/data/.hermes/home"}, map[string]any{"name": "API_SERVER_ENABLED", "value": "true"}, map[string]any{"name": "API_SERVER_HOST", "value": "0.0.0.0"}, diff --git a/internal/serviceoffercontroller/agent_render_test.go b/internal/serviceoffercontroller/agent_render_test.go index 91fce353..87b280f9 100644 --- a/internal/serviceoffercontroller/agent_render_test.go +++ b/internal/serviceoffercontroller/agent_render_test.go @@ -113,6 +113,9 @@ func TestAgentManifests_DeploymentEnvCarriesContext(t *testing.T) { "AGENT_NAMESPACE": "agent-quant", "AGENT_WALLET_ADDRESS": agent.Status.WalletAddress, "OBOL_SKILLS_DIR": "/data/.hermes/obol-skills", + // Must track HERMES_HOME: the upstream image bakes /opt/data and + // denies all file-tool writes outside the safe root. + "HERMES_WRITE_SAFE_ROOT": "/data/.hermes:/tmp", } got := make(map[string]string) for _, e := range envs {