diff --git a/services/hermes/deployment.yaml b/services/hermes/deployment.yaml index d2d299542..3fe15c1a0 100644 --- a/services/hermes/deployment.yaml +++ b/services/hermes/deployment.yaml @@ -25,6 +25,22 @@ spec: ai.bstein.dev/role: testing-triage ai.bstein.dev/placement: arm64 gateway lane (rpi5 preferred) ai.bstein.dev/config-rev: "20260804-root-operator-docs" + # The Anthropic credential comes from Vault rather than a manually + # created Secret. The role is declared in + # services/vault/scripts/vault_k8s_auth_configure.sh and bound to + # this pod's service account. + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/role: hermes + vault.hashicorp.com/agent-inject-secret-anthropic-token: kv/data/atlas/hermes/agent-tokens + vault.hashicorp.com/agent-inject-template-anthropic-token: | + {{- with secret "kv/data/atlas/hermes/agent-tokens" -}} + {{ .Data.data.anthropic_oauth_token }} + {{- end }} + vault.hashicorp.com/agent-pre-populate-only: "true" + vault.hashicorp.com/agent-requests-cpu: 25m + vault.hashicorp.com/agent-requests-mem: 32Mi + vault.hashicorp.com/agent-limits-cpu: 100m + vault.hashicorp.com/agent-limits-mem: 128Mi spec: serviceAccountName: hermes-triage automountServiceAccountToken: true @@ -88,6 +104,13 @@ spec: name: hermes-api-server-key key: api-key optional: true + # Fallback only; Vault is preferred when its file is present. + - name: CLAUDE_CODE_OAUTH_TOKEN_SEED + valueFrom: + secretKeyRef: + name: hermes-anthropic-token + key: token + optional: true command: - sh - -c @@ -115,6 +138,20 @@ spec: api_key="$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | od -An -tx1 | tr -d ' \n')" printf '\nAPI_SERVER_KEY=%s\n' "${api_key}" >> /opt/data/.env fi + # Anthropic credential: Vault first, then the manual Secret. The + # Secret is retained only as a rollback path while the migration + # settles; delete it once Vault has been serving for a while. + anthropic="" + if [ -r /vault/secrets/anthropic-token ]; then + anthropic="$(cat /vault/secrets/anthropic-token)" + elif [ -n "${CLAUDE_CODE_OAUTH_TOKEN_SEED:-}" ]; then + anthropic="${CLAUDE_CODE_OAUTH_TOKEN_SEED}" + fi + if [ -n "${anthropic}" ]; then + grep -v '^CLAUDE_CODE_OAUTH_TOKEN=' /opt/data/.env > /opt/data/.env.tmp || true + printf 'CLAUDE_CODE_OAUTH_TOKEN=%s\n' "${anthropic}" >> /opt/data/.env.tmp + mv /opt/data/.env.tmp /opt/data/.env + fi chmod 0600 /opt/data/.env chown -R 10000:10000 /opt/data securityContext: @@ -218,12 +255,7 @@ spec: # falls back to openai-codex. # TODO: migrate to Vault alongside the Gitea token; this manual # Secret was created in the interest of demo time. - - name: CLAUDE_CODE_OAUTH_TOKEN - valueFrom: - secretKeyRef: - name: hermes-anthropic-token - key: token - optional: true + volumeMounts: - name: home mountPath: /opt/data diff --git a/services/vault/scripts/vault_k8s_auth_configure.sh b/services/vault/scripts/vault_k8s_auth_configure.sh index cc5903b34..191acaa6c 100644 --- a/services/vault/scripts/vault_k8s_auth_configure.sh +++ b/services/vault/scripts/vault_k8s_auth_configure.sh @@ -253,8 +253,8 @@ write_policy_and_role "health" "health" "health-vault-sync" \ "health/*" "" write_policy_and_role "game-stream" "game-stream" "game-stream-vault" \ "game-stream/*" "" -write_policy_and_role "hermes" "hermes" "hermes-vault" \ - "hermes/operator-oidc" "" +write_policy_and_role "hermes" "hermes" "hermes-vault,hermes-triage" \ + "hermes/operator-oidc hermes/agent-tokens" "" write_policy_and_role "veles" "veles" "veles-backend,veles-generator,veles-postgres,veles-vault-sync" \ "veles/* shared/harbor-pull shared/postmark-relay" "" write_policy_and_role "veles-sim" "veles" "veles-sim" \