Skip to content

Helm chart 0.8.2: agents.<name>.env duplicates auto-promoted secretKeyRef entries -- Deployment apply fails #789

@howie

Description

@howie

Summary

When a user sets BOTH agents.<name>.discord.botToken (or .slack.botToken / .slack.appToken / .stt.apiKey / .gateway.token) AND the same key in agents.<name>.env.*, the rendered Deployment ends up with duplicate env entries for the same key. Kubernetes rejects the apply:

failed to create typed patch object (default/openab-claude; apps/v1, Kind=Deployment): errors:
  .spec.template.spec.containers[name="openab"].env: duplicate entries for key [name="DISCORD_BOT_TOKEN"]
  .spec.template.spec.containers[name="openab"].env: duplicate entries for key [name="SLACK_BOT_TOKEN"]

Chart 0.8.2, k8s 1.32 (OrbStack), helm 3.18.x.

Why users hit this

Per the existing workaround for agent-subprocess env isolation (see related #699), users have to re-declare bot tokens in agents.<name>.env.* so the subprocess can read them. But they also need to keep discord.botToken / slack.botToken set so the chart's other paths (secret.yaml, etc.) work. Both paths feed the Deployment's env: array, creating duplicates.

Concrete helm-install.sh pattern that triggers the bug:

helm upgrade --install openab openab/openab \
  -f values.yaml \
  --set agents.claude.discord.botToken="$CLAUDE_DISCORD_BOT_TOKEN" \
  --set agents.claude.slack.botToken="$CLAUDE_SLACK_BOT_TOKEN" \
  --set-string agents.claude.env.DISCORD_BOT_TOKEN="$CLAUDE_DISCORD_BOT_TOKEN" \
  --set-string agents.claude.env.SLACK_BOT_TOKEN="$CLAUDE_SLACK_BOT_TOKEN"

The chart's templates/deployment.yaml lines 44-65 auto-promote .discord.botToken / .slack.botToken / .slack.appToken / .stt.apiKey / .gateway.token into container env via valueFrom.secretKeyRef. Lines 82-85 then loop $cfg.env and emit each key as inline value:. Same key, two entries.

Repro

values:

agents:
  claude:
    discord:
      botToken: <any-token>
    slack:
      enabled: true
      botToken: <any-token>
    env:
      DISCORD_BOT_TOKEN: <same-token>
      SLACK_BOT_TOKEN: <same-token>

Run helm upgrade --install ... → apply fails with duplicate-key.

Proposed fix (chart-side, minimal)

In templates/deployment.yaml, guard each auto-promotion block with a hasKey check against agents.<name>.env. When the user explicitly sets the env var, skip the secretKeyRef auto-promotion (the explicit value wins).

Diff:

       env:
+        {{- $explicitEnv := $cfg.env | default dict }}
-        {{- if and (ne (toString ($cfg.discord).enabled) "false") ($cfg.discord).botToken }}
+        {{- if and (ne (toString ($cfg.discord).enabled) "false") ($cfg.discord).botToken (not (hasKey $explicitEnv "DISCORD_BOT_TOKEN")) }}
         - name: DISCORD_BOT_TOKEN
           valueFrom:
             secretKeyRef:
               name: {{ include "openab.agentFullname" $d }}
               key: discord-bot-token
         {{- end }}
-        {{- if and ($cfg.slack).enabled ($cfg.slack).botToken }}
+        {{- if and ($cfg.slack).enabled ($cfg.slack).botToken (not (hasKey $explicitEnv "SLACK_BOT_TOKEN")) }}
         - name: SLACK_BOT_TOKEN
           ...
         {{- end }}
         {{- /* same hasKey guard added to SLACK_APP_TOKEN, STT_API_KEY, GATEWAY_WS_TOKEN */ -}}

Verified locally against chart 0.8.2 — patched deployment.yaml renders cleanly with both discord.botToken and env.DISCORD_BOT_TOKEN set, no duplicate.

Trade-off: when the user picks the explicit-env path, the token gets inlined into the Deployment spec (visible via kubectl get deploy -o yaml) instead of staying inside a Secret. Slightly less secure, but the user opted in by explicitly setting env.<KEY>.

A cleaner alternative (bigger schema change)

Add a agents.<name>.subprocessEnv map that gets passed through OpenAB's [agent].env config (so the subprocess sees it) without touching the container env. Then agents.<name>.env stays the place for container-level overrides, and the two never collide. This avoids the inline-token security trade-off but requires changes to both chart and OpenAB router config schema. This would also subsume the inherit_env feature request in #699 (CLOSED).

Related

Offer

Happy to send a PR for the minimal fix above. If the cleaner subprocessEnv direction is preferred I can sketch that too — needs guidance on the router-side schema.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions