From cb1b411276e7cb7869df7b414ff49f1563345804 Mon Sep 17 00:00:00 2001 From: Paul O'Reilly Date: Thu, 7 May 2026 13:14:21 +1200 Subject: [PATCH] fix(minimax): wire api_key via apiKeyHelper, never via env Mirrors agent-runtimes commit 1b83c81. CRS serves these harness files to dispatchers, so this repo must match for the fix to take effect. The minimax harness now writes ~/.claude/settings.json with an apiKeyHelper that `cat`s the ESO-mounted /run/agent/secrets/minimax/ api_key file. Claude Code routes apiKeyHelper output to `Authorization: Bearer ` for non-anthropic.com base URLs, which is what MiniMax's /anthropic proxy requires. The secret is never read into env, never written into a config file, and ESO rotation is auto-recovered via Claude Code's per-session helper invocation (also re-runs after a 401). - harness.yaml: drop ANTHROPIC_AUTH_TOKEN_FILE env var - init.sh: write (or jq-merge) settings.json with apiKeyHelper, chmod 0600 - bin/anthropic-compat-wrapper.sh: deleted Co-Authored-By: Claude Opus 4.7 --- .../v1/bin/anthropic-compat-wrapper.sh | 9 ---- harnesses/contexts/minimax/v1/harness.yaml | 4 +- harnesses/contexts/minimax/v1/init.sh | 54 ++++++++++++++++--- 3 files changed, 49 insertions(+), 18 deletions(-) delete mode 100755 harnesses/contexts/minimax/v1/bin/anthropic-compat-wrapper.sh diff --git a/harnesses/contexts/minimax/v1/bin/anthropic-compat-wrapper.sh b/harnesses/contexts/minimax/v1/bin/anthropic-compat-wrapper.sh deleted file mode 100755 index a0b11c3..0000000 --- a/harnesses/contexts/minimax/v1/bin/anthropic-compat-wrapper.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# Anthropic-compat wrapper for MiniMax (Phase 9 ESO-managed secrets). -# Reads the api_key file and passes it as ANTHROPIC_AUTH_TOKEN to claude. -# ANTHROPIC_BASE_URL is set in harness.yaml `env:` and flows through. -set -euo pipefail - -exec env \ - ANTHROPIC_AUTH_TOKEN="$(cat /run/agent/secrets/minimax/api_key)" \ - claude "$@" diff --git a/harnesses/contexts/minimax/v1/harness.yaml b/harnesses/contexts/minimax/v1/harness.yaml index c7d99e2..9789e83 100644 --- a/harnesses/contexts/minimax/v1/harness.yaml +++ b/harnesses/contexts/minimax/v1/harness.yaml @@ -5,9 +5,11 @@ description: "MiniMax coding plan — Anthropic-compatible proxy" requires: [] provides: [claude-code] +# Auth is wired by init.sh via Claude Code's apiKeyHelper (settings.json). +# No credential env vars: the secret stays in the mounted file and is read +# only by the helper command at request time. env: ANTHROPIC_BASE_URL: "https://api.minimax.io/anthropic" - ANTHROPIC_AUTH_TOKEN_FILE: "/run/agent/secrets/minimax/api_key" CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1" CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" DISABLE_PROMPT_CACHING: "1" diff --git a/harnesses/contexts/minimax/v1/init.sh b/harnesses/contexts/minimax/v1/init.sh index 4fcd3f2..72bb818 100755 --- a/harnesses/contexts/minimax/v1/init.sh +++ b/harnesses/contexts/minimax/v1/init.sh @@ -1,12 +1,24 @@ #!/bin/bash -set -euo pipefail - -# Verify the ESO-managed MiniMax api_key file is mounted. The wrapper -# (`bin/anthropic-compat-wrapper.sh`) reads it at exec time and exports -# ANTHROPIC_AUTH_TOKEN to the claude subprocess. +# minimax init — write ~/.claude/settings.json with apiKeyHelper. # -# ANTHROPIC_BASE_URL is set in harness.yaml `env:` (not in the K8s Secret), -# so no file check is required for it. +# The MiniMax API key is mounted by ESO at /run/agent/secrets/minimax/api_key +# (mode 0400, secrets_required entry in harness.yaml). Claude Code's +# `apiKeyHelper` setting names a command that prints the key on stdout when +# the CLI needs it for an API request — the value never enters this process' +# environment, never appears in /proc//environ of the claude subprocess, +# and is read fresh on each invocation so ESO secret rotations are picked up +# without a process restart. +# +# Why settings.json (not --settings inline or env vars): +# - ANTHROPIC_AUTH_TOKEN / ANTHROPIC_API_KEY in env exposes the secret in +# /proc//environ, log aggregators, ps. The H-SECRET-4 rule disallows +# credential-shaped env vars for that reason. +# - --settings on the runner CLI line couples the runner to the harness +# layout. Per-harness settings.json keeps auth a harness concern. +# - The helper command (`cat `) re-reads on each call, so secret +# rotation propagates without rewriting the config file. + +set -euo pipefail API_KEY_FILE="/run/agent/secrets/minimax/api_key" @@ -15,4 +27,30 @@ if [ ! -r "$API_KEY_FILE" ]; then exit 1 fi -echo "minimax api_key verified at $API_KEY_FILE" +# Resolve the agent's home and write settings.json there. CLAUDE_CONFIG_DIR +# overrides ~/.claude when set; honour it so non-default profiles work. +CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" +mkdir -p "$CONFIG_DIR" +chmod 0700 "$CONFIG_DIR" + +SETTINGS_FILE="$CONFIG_DIR/settings.json" + +# If a settings.json already exists from another harness layer, merge +# apiKeyHelper into it; otherwise create a minimal file. jq is in the base +# image; fall back to a clean overwrite if it isn't available for any reason. +if [ -f "$SETTINGS_FILE" ] && command -v jq >/dev/null 2>&1; then + TMP=$(mktemp) + jq --arg helper "cat $API_KEY_FILE" \ + '. + {apiKeyHelper: $helper}' \ + "$SETTINGS_FILE" > "$TMP" + mv "$TMP" "$SETTINGS_FILE" +else + cat > "$SETTINGS_FILE" <