diff --git a/CLAUDE.md b/CLAUDE.md index af16b1e..f4d4458 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -99,7 +99,7 @@ layers: ### No secrets in this repo -This is a public framework repo. Secrets (provider API keys, SOPS-encrypted env files) belong in per-installation private repos attached at a lower CRS priority. The `z-ai/v1`, `minimax/v1`, `alibaba-model-studio/v1` contexts here declare the structure but leave `provider.sops.env` to the installation repo. +This is a public framework repo. Secrets (provider API keys) live in OpenBao via M22 ESO, never in the framework. The `minimax/v1` context here declares the structure; the live credential is mounted by ESO from the per-installation OpenBao instance. (Z.ai and Alibaba Model Studio contexts are retired but revivable — see agent-runtimes `planning/future/providers` F49 / F50.) ## Adding a New Harness Context diff --git a/harnesses/contexts/gitea-admin/v1/init.sh b/harnesses/contexts/gitea-admin/v1/init.sh index a84ed64..7c9fb9d 100755 --- a/harnesses/contexts/gitea-admin/v1/init.sh +++ b/harnesses/contexts/gitea-admin/v1/init.sh @@ -13,7 +13,7 @@ # This was previously broken: the wrapper `cat`d the ESO mount path which # the agent could not read. Discovered during the M22 Phase 9 audit; the # wrapper-script approach was never re-validated post-cutover. Stage-and- -# wrapper-points-at-stage matches gitea-ssh / gitea-https / minimax / z-ai. +# wrapper-points-at-stage matches gitea-ssh / gitea-https / minimax. # # Rotation handling: per-container init. Long-running sessions need a # future scripts.control_loop hook to re-stage between operations. diff --git a/harnesses/contexts/gitea-https/v1/init.sh b/harnesses/contexts/gitea-https/v1/init.sh index 68a3c7a..08624d0 100755 --- a/harnesses/contexts/gitea-https/v1/init.sh +++ b/harnesses/contexts/gitea-https/v1/init.sh @@ -12,7 +12,7 @@ # inside an init.sh-generated helper script, which silently failed because # the helper runs as agent and the ESO file is root:root mode 0400. The # stage-and-helper-points-at-stage pattern matches gitea-ssh / minimax / -# z-ai / anthropic-cloud-paul-oauth. +# anthropic-cloud-paul-oauth. # # Rotation handling: per-container init. Ephemeral container agents # always pick up the latest mounted token. Long-running sessions need a diff --git a/harnesses/contexts/z-ai/v1/harness.yaml b/harnesses/contexts/z-ai/v1/harness.yaml deleted file mode 100644 index 478bd60..0000000 --- a/harnesses/contexts/z-ai/v1/harness.yaml +++ /dev/null @@ -1,28 +0,0 @@ -kind: context -name: z-ai -version: 1 -description: "Z.ai GLM coding plan — Anthropic-compatible proxy" -requires: [] -provides: [claude-code] - -env: - ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic" - CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1" - CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" - DISABLE_PROMPT_CACHING: "1" - -secrets_required: - - name: z-ai - account_ref: "z-ai" - mount_path: /run/agent/secrets/z-ai - # 0400 (root-only) — defense in depth. The agent user cannot read this - # mount. init.sh runs as root and stages the auth_token into the agent's - # home with mode 0600. apiKeyHelper points at the staged copy. Matches - # the gitea-ssh pattern; if the ESO Secret later carries additional - # files (e.g. base_url is already there), they remain inaccessible. - mode: "0400" - -scripts: - init: "./init.sh" - -# TODO: Add network_hosts for api.z.ai when context harnesses support it \ No newline at end of file diff --git a/harnesses/contexts/z-ai/v1/init.sh b/harnesses/contexts/z-ai/v1/init.sh deleted file mode 100755 index f2e75bd..0000000 --- a/harnesses/contexts/z-ai/v1/init.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -# z-ai init — stage the auth_token for the agent user and wire apiKeyHelper. -# -# Threat model: keep the ESO mount root-only (mode 0400) so the agent user -# cannot directly read /run/agent/secrets/z-ai/*. init.sh runs as root and -# stages a single per-secret copy of auth_token into the agent's home with -# mode 0600. Only that staged file is reachable by the runtime; any other -# files in the ESO Secret (e.g. legacy base_url) stay root-only. -# -# Rotation handling: this is a one-shot copy at container start. Ephemeral -# container agents always run init.sh per task — no rotation gap there. -# Long-running sessions need a future scripts.control_loop hook to refresh -# the copy between agent CLI invocations. -# -# Auth wire-up: apiKeyHelper output routes to `Authorization: Bearer ` -# when ANTHROPIC_BASE_URL is non-anthropic.com (set in harness.yaml to -# https://api.z.ai/api/anthropic). The secret value never enters env or any -# process' /proc//environ. - -set -euo pipefail - -ESO_AUTH_TOKEN="/run/agent/secrets/z-ai/auth_token" - -if [ ! -r "$ESO_AUTH_TOKEN" ]; then - echo "ERROR: $ESO_AUTH_TOKEN not readable. Check ESO ExternalSecret acct-." >&2 - exit 1 -fi - -AGENT_USER="${AGENT_USER:-agent}" -AGENT_HOME=$(getent passwd "$AGENT_USER" | cut -d: -f6) -if [ -z "$AGENT_HOME" ] || [ ! -d "$AGENT_HOME" ]; then - AGENT_HOME="/home/$AGENT_USER" -fi - -# Stage the auth_token into a per-secret path owned by agent, mode 0600. -STAGED_KEY_DIR="$AGENT_HOME/.claude/secrets" -STAGED_KEY="$STAGED_KEY_DIR/z-ai-auth-token" -mkdir -p "$STAGED_KEY_DIR" -chown "$AGENT_USER:" "$STAGED_KEY_DIR" 2>/dev/null || true -chmod 0700 "$STAGED_KEY_DIR" -install -m 0600 -o "$AGENT_USER" -g "$AGENT_USER" "$ESO_AUTH_TOKEN" "$STAGED_KEY" - -# Wire apiKeyHelper at the staged copy. -CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$AGENT_HOME/.claude}" -mkdir -p "$CONFIG_DIR" -chown "$AGENT_USER:" "$CONFIG_DIR" 2>/dev/null || true -chmod 0755 "$CONFIG_DIR" - -SETTINGS_FILE="$CONFIG_DIR/settings.json" - -if [ -f "$SETTINGS_FILE" ] && command -v jq >/dev/null 2>&1; then - TMP=$(mktemp) - jq --arg helper "cat $STAGED_KEY" \ - '. + {apiKeyHelper: $helper}' \ - "$SETTINGS_FILE" > "$TMP" - mv "$TMP" "$SETTINGS_FILE" -else - cat > "$SETTINGS_FILE" </dev/null || true -chmod 0644 "$SETTINGS_FILE" - -echo "z-ai auth_token staged at $STAGED_KEY (0600 $AGENT_USER:$AGENT_USER)" -echo "z-ai apiKeyHelper wired in $SETTINGS_FILE"