# v0.1 Secret 与 provider credential 分发规格 本文定义 AgentRun `v0.1` 的 Secret 和 Code Agent provider credential 分发边界。真实 Code Agent backend 需要上游模型凭据;Codex stdio profile 测试凭据以 `~/.codex/auth.json`、`~/.codex/config.toml` 和必要的 profile-local model catalog 形态为输入源,通过 Kubernetes Secret 投影进入 runner/backend Pod。这些值不得进入 Git source、GitOps branch、artifact catalog、event、trace、日志或 CLI 输出。 在装配 SPEC 中,本文承担 SecretRef、projection、rotation 和 redaction 规则;运行时 credential 必须先归入 `ProfileRef`、`ResourceBundleRef.credentialRef` 或 `executionPolicy.secretScope.toolCredentials[]`,再由 runner Job 装配。权威装配模型见 [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)。 ## 设计目标 - API Key、Codex auth/config 等 provider credential 只通过 Kubernetes SecretRef 分发到需要它的 manager、runner 或 backend adapter。 - `deploy/deploy.json` 只记录 SecretRef 名称、key 名称、mount/env intent 和 secret scope,不记录 Secret 值。 - `v0.1-gitops` 的 rendered manifests 只能引用 Secret 名称和 key,不包含 Secret data。 - `agentrun-mgr` 保存 run 的 `executionPolicy.secretScope`,但保存的是 credential source reference,不是 credential value。 - runner 和 backend adapter 只能消费 manager 已授权的 secret scope,不能枚举 namespace 内所有 Secret。 ## Secret 分类 | Secret 类别 | 用途 | 默认消费者 | v0.1 规则 | | --- | --- | --- | --- | | Postgres DSN | manager 连接 durable store | `agentrun-mgr` | 只通过 `agentrun-v01-mgr-db/DATABASE_URL` 注入。 | | Codex stdio profile 凭据文件 | 真实 Code Agent backend 调上游模型 | runner 或 backend adapter | `codex`、`deepseek` 与 `minimax-m3` 使用 `auth.json`/`config.toml` 文件形态;`dsflash-go` 额外要求 `model-catalog.json`。这些文件只通过 profile-scoped Kubernetes SecretRef 文件投影注入,不写入 run payload。 | | Git SSH deploy key | Tekton checkout source/GitOps promotion,Argo 读取 GitOps branch | Tekton、Argo CD | 只存在于 `agentrun-ci` 或 `argocd` Secret;不进入 runtime Pod。 | | Registry credential | push/pull private registry | Tekton、runtime imagePullSecret | 只作为 ServiceAccount/imagePullSecret 引用。 | | Tool credential | GitHub PR、issue、UniDesk SSH passthrough、artifact registry 等 agent shell/tool 授权 | runner/backend adapter | 必须通过 `executionPolicy.secretScope.toolCredentials[]` 的 SecretRef 装配进入运行时;不是 Queue integration,也不能用 `transientEnv` 承载长期 credential。 | | Future tenant credential | tenant 专属工具或外部服务 | runner/backend adapter | 必须先扩展装配 SPEC 的 SecretRef 和 secret scope,再允许 run 引用。 | ## 固定命名建议 | 对象 | v0.1 建议 | | --- | --- | | Manager DB Secret | `agentrun-v01-mgr-db` key `DATABASE_URL` | | Codex Provider Secret | `agentrun-v01-provider-codex` keys `auth.json`、`config.toml` | | DeepSeek Provider Secret | `agentrun-v01-provider-deepseek` keys `auth.json`、`config.toml` | | MiniMax-M3 Provider Secret | `agentrun-v01-provider-minimax-m3` keys `auth.json`、`config.toml` | | dsflash-go Provider Secret | `agentrun-v01-provider-dsflash-go` keys `auth.json`、`config.toml`、`model-catalog.json` | | Provider projection target | 只读 `/var/run/agentrun/secrets/-/auth.json`、`config.toml` 和 profile 需要的额外文件,再复制到当前 run/profile 的 writable `CODEX_HOME` | | Provider config | 非敏感 base URL/model 可以来自 `config.toml` 或 ConfigMap;credential value 不得放入 ConfigMap。 | | Tekton Git SSH Secret | `agentrun-ci/agentrun-git-ssh` | | Argo Git SSH Secret | `argocd/agentrun-git-ssh` | | GitHub PR Tool Secret | `agentrun-v01-tool-github-pr` key `GH_TOKEN` | | GitHub SSH Tool Secret | `agentrun-v01-tool-github-ssh` keys `id_ed25519`、`known_hosts`、`config` | | UniDesk SSH Tool Secret | `agentrun-v01-tool-unidesk-ssh` key `UNIDESK_SSH_CLIENT_TOKEN` | | Runtime ServiceAccount | `agentrun-v01-mgr`、`agentrun-v01-runner` | 命名可以在实现时因集群约束调整,但必须满足 lane 独立、用途单一、最小 RBAC 和不跨 `v0.1`/`v0.2` 复用的原则。 ## Codex 测试凭据注入 `v0.1` 综合联调使用的 Codex stdio profile 测试凭据源固定为 operator 环境中的文件形态: ```text ~/.codex/auth.json ~/.codex/config.toml ``` 这两个文件只能作为 Kubernetes Secret 创建或轮换的输入源。`codex` profile 默认使用 operator 当前 Codex 配置;`deepseek` profile 使用 operator 准备的 DeepSeek-compatible Codex 配置,可以来自另一个 `--codex-home` 或显式 `--auth-file`/`--config-file`;`minimax-m3` profile 使用从 HWLAB Code Queue 现有 MiniMax API key 派生的 Codex 配置,模型固定为 `MiniMax-M3`,wire API 使用当前 Codex app-server 支持的 `responses`;`dsflash-go` 使用 `deepseek-v4-flash`、1M/900k context 和 `model-catalog.json`。禁止把宿主机 `~/.codex` 以 hostPath 挂入 runner/backend Pod,禁止复制进镜像,禁止提交到 source branch、GitOps branch、artifact catalog、issue、PR、event、trace、日志或 CLI 输出。 默认 Secret projection 规则: | 项目 | v0.1 规格 | | --- | --- | | Kubernetes Secret | `agentrun-v01/agentrun-v01-provider-codex`、`agentrun-v01/agentrun-v01-provider-deepseek`、`agentrun-v01/agentrun-v01-provider-minimax-m3` 或 `agentrun-v01/agentrun-v01-provider-dsflash-go` | | Secret key | `auth.json`,来自 `~/.codex/auth.json` | | Secret key | `config.toml`,来自 `~/.codex/config.toml` | | Secret key | `model-catalog.json`,仅 `dsflash-go` 必需,必须与 `config.toml` 的 `model_catalog_json` 指向同一 runtime 路径。 | | Projection path | 只读 Secret projection 挂到 `/var/run/agentrun/secrets/-/auth.json`、`config.toml` 和 profile 需要的额外文件;该路径只作为 credential source。 | | Runtime config path | runner 启动时把当前 `backendProfile` 授权的 Secret projection 复制到 writable `CODEX_HOME`,Kubernetes Job 默认使用该 Job 独占的 `/home/agentrun/.codex-/auth.json`、`config.toml` 和 profile 需要的额外文件;复用进程必须使用 run/profile 独占目录。 | | Projection mode | 只读,建议 `0400` 或等价最小权限 | | Runtime env | `HOME=/home/agentrun`,`CODEX_HOME=/home/agentrun/.codex-`,`AGENTRUN_CODEX_SECRET_HOME=`;不得 fallback 到节点宿主机 home。 | Secret 创建和轮换必须通过 Kubernetes 密钥管理完成。`deploy/deploy.json` 只写 SecretRef 名称、key 和 mount intent;`v0.1-gitops` rendered manifests 只引用 Secret,不包含 Secret data。 Provider API Key 的 Web 配置由 [spec-v01-provider-profile-management.md](spec-v01-provider-profile-management.md) 定义:HWLAB Cloud API 鉴权后委托 AgentRun manager 更新受控 profile Secret/config。该路径仍只写 Kubernetes Secret data,不把 API Key 写入 Git、GitOps、Postgres、event、trace、日志或 CLI 输出。 ## Run secretScope 合同 Run 的 `executionPolicy.secretScope` 只能包含引用,不包含值。provider credential 使用 `providerCredentials[]`;GitHub PR、UniDesk SSH passthrough 等 agent shell/tool 授权使用装配 SPEC 定义的 `toolCredentials[]`,不得混入 `transientEnv`。示例形态: ```json { "providerCredentials": [ { "profile": "codex", "secretRef": { "namespace": "agentrun-v01", "name": "agentrun-v01-provider-codex", "keys": ["auth.json", "config.toml"], "mountPath": "~/.codex" } }, { "profile": "deepseek", "secretRef": { "namespace": "agentrun-v01", "name": "agentrun-v01-provider-deepseek", "keys": ["auth.json", "config.toml"], "mountPath": "~/.codex" } }, { "profile": "minimax-m3", "secretRef": { "namespace": "agentrun-v01", "name": "agentrun-v01-provider-minimax-m3", "keys": ["auth.json", "config.toml"], "mountPath": "~/.codex" } }, { "profile": "dsflash-go", "secretRef": { "namespace": "agentrun-v01", "name": "agentrun-v01-provider-dsflash-go", "keys": ["auth.json", "config.toml", "model-catalog.json"], "mountPath": "~/.codex" } } ], "allowCredentialEcho": false } ``` 规则: - `allowCredentialEcho` 必须固定为 `false`。 - `secretRef.namespace` 默认只能是 run 所在 lane namespace 或明确批准的 platform namespace。 - manager 可以保存 `secretRef`,但不得读取 Secret 值后存库。 - runner/backend adapter 获得 Secret 的方式必须来自 Kubernetes env/file projection 或受限 Secret API 读取;Codex 默认从只读 Secret projection 复制 `auth.json`、`config.toml` 和 profile 需要的额外文件到 writable `CODEX_HOME` 后启动 app-server,不得通过 run payload、event、CLI 参数或日志传递。 - runner/backend adapter 只能选择与 run `backendProfile` 同名的 provider credential;`backendProfile=deepseek`、`backendProfile=minimax-m3` 或 `backendProfile=dsflash-go` 缺少 matching SecretRef 时必须 `secret-unavailable`,不得 fallback 到 `codex` 或另一个 profile。 - manager 会按内建 profile 的 `requiredSecretKeys` 规范化 `secretRef.keys`;消费侧旧 payload 即使只提交 `auth.json`/`config.toml`,`dsflash-go` runner Job 也必须投影 `model-catalog.json`。若实际 Kubernetes Secret 缺少该 key,run 必须在装配或 readiness 阶段失败为结构化 `secret-unavailable`。 - Secret projection 不能直接作为 `CODEX_HOME`。Codex app-server 会读取并可能维护默认配置、PATH 或运行态文件;把只读 Secret volume 直接挂到 `CODEX_HOME` 会造成启动期写入失败。v0.1 的固定边界是:Secret volume 只读、`/home/agentrun` 由 `emptyDir` 提供可写 runtime home、复制动作只发生在 runner/backend 容器内且不打印文件内容。 - SecretRef 不存在或 RBAC 不允许时,run 必须失败为结构化 `failureKind=secret-unavailable` 或等价错误,不得降级成无凭证重试风暴。 - `toolCredentials` 的 SecretRef/projection/redaction 规则以 [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md) 为准;本文只约束 Secret value 不落库、不输出、不进入 Git source 或 GitOps Secret data。 ## runner-job transientEnv `transientEnv` 用于承接调度方生成的短期、单次 runner Job 运行上下文,例如 HWLAB Code Agent 的 owner-scoped HWPOD/runtime API key、runtime URL 和非敏感 UniDesk frontend 地址。它也可以承接 manager 因 `tool=unidesk-ssh` 自动补齐的受控默认 endpoint env。它不是 provider credential、tool credential,也不是 run durable fact。 规则: - `transientEnv` 只能出现在 `POST /api/v1/runs/:runId/runner-jobs` 请求中;不得写入 `CreateRunInput`、command payload、event payload 或 result envelope 的 value 明文。 - manager 不对 `transientEnv` 条目数量设固定上限,只校验数组形态、env name 合法且唯一、value 非空和单值长度;runner job payload hash 只纳入 env name 与 value hash。 - 当 run 请求 `executionPolicy.secretScope.toolCredentials[].tool=unidesk-ssh` 且调用方未提供 `UNIDESK_MAIN_SERVER_IP`、`UNIDESK_MAIN_SERVER_HOST` 或 `UNIDESK_FRONTEND_URL` 时,manager 可以从受控默认配置自动补齐一个非敏感 endpoint env。该自动补齐必须遵守同一 redaction 规则,只显示 name/count/hash 和 `valuesPrinted=false`。 - response、runner job status、event 和 dry-run manifest 只能展示 env name、count、SecretRef metadata 和 `valuesPrinted=false`;dry-run manifest 中的 transient env value 必须显示为 `REDACTED`。 - 正式 Kubernetes runner Job 不得把 `transientEnv` value 作为 pod spec plain env value。manager 必须先创建本次 Job 专属的短期 Kubernetes Secret,再在 runner container env 中使用 `valueFrom.secretKeyRef` 引用对应 key。Secret name、namespace、keys、ownerReference attached 状态和 `valuesPrinted=false` 可以出现在 response/event/trace 中,Secret data 和 env value 不得出现。 - 本次 Job 专属 transient env Secret 必须带有 run/command/attempt/runner/job metadata,创建 Job 失败时清理已创建 Secret;Job 创建成功后尽量给 Secret 加上 ownerReference,让 Kubernetes 随 Job GC。ownerReference patch 失败必须以 warning 暴露,不能回退为 plain env value。 - `transientEnv` token 必须由调度方控制 TTL、权限和业务授权范围;AgentRun 只负责本次 runner Job 的短期投影和 redaction,不把它升级为长期 credential。 - AgentRun 不解释 HWLAB HWPOD 权限,也不把业务鉴权做成通用 policy;AgentRun 只负责不持久化、不回显、不扩散这类短期 env value。 - GitHub token、UniDesk SSH client token、SSH private key、provider API key、registry token 等 provider/tool 可复用 credential 不得通过 `transientEnv` 注入;必须先进入装配 SPEC 的 SecretRef 路径。HWLAB dispatcher 生成并限定 owner/HWPOD/runtime scope 的 `HWLAB_API_KEY` 属于业务运行上下文,可以作为单次 runner Job env 透传,但不得变成 AgentRun durable fact 或通用授权。 ## 分发路径 `v0.1` 默认路径: ```text deploy/deploy.json -> declares SecretRef names and mount/env intent only Tekton promotion -> renders SecretRef references into v0.1-gitops manifests Argo CD -> syncs workload references to agentrun-v01 Kubernetes Secret -> created from profile-specific auth.json, config.toml and required extra files by operator or approved secret-management flow runner/backend Pod -> receives Codex auth/config/model catalog via read-only file projection -> copies authorized files into writable CODEX_HOME before starting Codex app-server ``` Secret 创建和轮换不由 source branch 自动生成;source branch 只声明需要哪个 SecretRef。后续如果接入 External Secrets、Vault、SealedSecrets 或 SOPS,必须新增或更新本 spec,明确 controller、source of truth、rotation 和 redaction 规则。 ## Codex Secret dry-run 工具 `v0.1` 提供只读 CLI 工具,用 operator 本地 `~/.codex/auth.json`、`~/.codex/config.toml` 和 profile 需要的额外文件构造 Kubernetes Secret 创建计划: ```bash ./scripts/agentrun secrets codex render --dry-run [--profile codex|deepseek|minimax-m3|dsflash-go] ``` 可选参数: - `--codex-home `:覆盖默认 `~/.codex` 输入目录。 - `--profile `:默认 `codex`;`deepseek`、`minimax-m3` 和 `dsflash-go` 使用同一文件形态但默认 Secret name 分别为对应 `agentrun-v01-provider-`。 - `--auth-file ` / `--config-file `:分别覆盖输入文件路径。 - `--model-catalog-file `:覆盖 `dsflash-go` 的 `model-catalog.json` 输入文件路径。 - `--namespace `:默认 `agentrun-v01`。 - `--secret-name `:默认随 profile 变化,`codex` 为 `agentrun-v01-provider-codex`,`deepseek` 为 `agentrun-v01-provider-deepseek`,`minimax-m3` 为 `agentrun-v01-provider-minimax-m3`。 输出必须是 JSON,并且只包含 `namespace`、`secretName`、`keys`、每个输入文件的 `bytes`、`sha256`/`contentHash`、整体 hash、redaction 状态、apply 命令形状和 Secret manifest 摘要。输出不得包含 Secret value、`auth.json` 明文、`config.toml` 明文、`model-catalog.json` 明文、base64 `data` 字段或可直接恢复 credential 的内容。工具只支持 `--dry-run`;不得执行 `kubectl apply`。 失败必须结构化返回 `failureKind`:缺文件、不可读文件或空 credential 归类为 `secret-unavailable`;非法 JSON/TOML 归类为 `schema-invalid`。 ## GitHub SSH Tool Secret bootstrap `Artificer` 需要 GitHub SSH 权限时,长期 credential 不进入 Queue payload、`transientEnv`、Git source 或 GitOps Secret data。v0.1 提供受控 manager/CLI 入口把 operator 已有 SSH 文件写入 runtime namespace Secret: ```bash ./scripts/agentrun tool-credentials set-github-ssh \ --private-key-file ~/.ssh/id_ed25519_github \ --known-hosts-file ~/.ssh/known_hosts \ --config-file ~/.ssh/config ``` 规则: - 写入对象固定为 `agentrun-v01/agentrun-v01-tool-github-ssh`,keys 固定为 `id_ed25519`、`known_hosts`、`config`。 - CLI dry-run 只显示输入文件 bytes、SecretRef、key 列表和确认命令,不输出文件内容。 - manager upsert Secret 只返回 resourceVersion、hash suffix、SecretRef 和 redaction 状态,不输出 `data`、`stringData`、private key、known_hosts 或 config 明文。 - `config` 缺省为只允许 `github.com` 走 `ssh.github.com:443`、`IdentityFile /home/agentrun/.ssh/id_ed25519`、`StrictHostKeyChecking yes` 和 `UserKnownHostsFile /home/agentrun/.ssh/known_hosts` 的最小配置;runner Job 同时注入 `GIT_SSH_COMMAND`,用绝对路径指向挂载的 config、identity 和 known_hosts,避免 OpenSSH 的 `~` 按容器 passwd home 解析到错误目录;若传入自定义 config,必须包含 `Host` 与 `IdentityFile`。 - runtime 消费仍必须通过 `executionPolicy.secretScope.toolCredentials[]` 的 volume projection 挂载到 `/home/agentrun/.ssh`;不得把同一 SSH private key 复制到镜像、ConfigMap、payload 或 transient env。 ## 日志与事件 Redaction - event、trace、日志、CLI 输出、health 和 diagnostics 不得打印 Secret 值。 - `Authorization`、`api_key`、`token`、`password`、URL credential、DSN password、Codex `auth.json` 和 `config.toml` 文件内容必须 redacted。 - 可以打印 SecretRef 名称、key 名称、credential source、是否存在、是否被挂载、是否通过 readiness 检查。 - provider auth 失败只能报告 failure kind、HTTP status 分类和 request id;不得打印请求 header 或 body 中的凭据。 ## 测试规格 ### T1 SecretRef render 阅读本文和 [spec-v01-cicd.md](spec-v01-cicd.md),然后检查 `deploy/deploy.json` 只包含 SecretRef 名称/key 和 mount/env intent;rendered GitOps manifest 也只包含 SecretRef,不包含 Secret data。 ### T2 Runner credential projection 阅读本文,然后分别启动 `backendProfile=codex`、`backendProfile=deepseek`、`backendProfile=minimax-m3` 与 `backendProfile=dsflash-go` 的最小 backend runner dry-run,确认 Pod file projection 挂在 `/var/run/agentrun/secrets/...` 且只读,`/home/agentrun` 是 writable runtime home,runner/backend 只把当前 profile 授权文件复制到 `CODEX_HOME` 后再启动 Codex;`dsflash-go` 必须同时投影并复制 `model-catalog.json`;event、日志和 CLI 输出只显示 redacted credential source,不显示文件内容。 ### T3 Missing secret failure 阅读本文,然后用一个不存在的 provider SecretRef 创建 run,确认 run 失败为结构化 `secret-unavailable`,不会打印 Secret 值,也不会无限重试。 ### T4 transientEnv SecretRef 投影 阅读本文,然后通过正式 `runner-jobs` 或 Queue dispatch 路径创建带 `transientEnv` 的真实 runner Job。检查 runner job response、event、dry-run manifest 和 Kubernetes Pod spec:response/event 只能输出 env names、Secret name/namespace/keys、ownerReference 状态和 `valuesPrinted=false`;Pod spec 中对应 env 必须是 `valueFrom.secretKeyRef`,不能包含 `value`;Secret data 只能通过 Kubernetes Secret 保存,验证时只允许读取 key 列表和 metadata,不得输出 value。 ## 规格的实现情况 | 规格项 | 状态 | 说明 | | --- | --- | --- | | Secret 分发规格 | 已定义 | 本文为 v0.1 provider credential 分发权威。 | | Kubernetes SecretRef 注入 | 已实现/已通过主闭环 | runner Job dry-run 和正式 Job 创建路径已按 run `executionPolicy.secretScope.providerCredentials` 生成 Secret volume projection、writable runtime home 和 `AGENTRUN_CODEX_SECRET_HOME`;真实 Secret 与 Codex turn 已通过主闭环。 | | Codex Secret dry-run 工具 | 已实现 | `./scripts/agentrun secrets codex render --dry-run` 只输出 Secret 创建计划、hash 和 redacted manifest 摘要,不执行 apply。 | | Codex auth/config file projection | 已实现主路径 | backend readiness 检查 `auth.json`/`config.toml` 可读性,缺失时返回 `secret-unavailable`;真实 runner Job 将只读 projection 复制到 writable `CODEX_HOME`。 | | DeepSeek profile SecretRef | 已实现/已通过主闭环 | 已新增 `agentrun-v01-provider-deepseek` render、GitOps/RBAC 引用、Job projection、profile 选择和负向 missing-secret 自测试;真实 Secret 创建与 Kubernetes Job projection 已通过主闭环,轮换仍由 Kubernetes 密钥管理流程完成。 | | MiniMax-M3 profile SecretRef | 已实现/待真实主闭环 | 已新增 `agentrun-v01-provider-minimax-m3` render、GitOps/RBAC 引用、Job projection、profile 选择和负向 missing-secret 自测试;真实 Secret 创建使用 HWLAB Code Queue 现有 MiniMax API key,轮换仍由 Kubernetes 密钥管理流程完成。 | | dsflash-go profile SecretRef | 已实现/待真实主闭环 | 已新增 `agentrun-v01-provider-dsflash-go` 的 `model-catalog.json` required key、Secret render、Job projection、writable `CODEX_HOME` 复制和负向 readiness;真实 profile turn 仍需按 provider 管理 canary 和 HWLAB 原入口复测。 | | Tool credential SecretRef | 已实现 env + volume projection | `executionPolicy.secretScope.toolCredentials[]` 已支持 `tool=github`、`tool=unidesk-ssh` 与 `projection.kind=env|volume`,runner Job 通过 Kubernetes `secretKeyRef` 注入 env,或通过只读 Secret volume 挂载到 `/home/agentrun/*`;CLI、event、runner job response 和 dry-run 只显示 SecretRef/projection 元数据,不输出值。 | | GitHub SSH Tool Secret bootstrap | 已实现 | `tool-credentials set-github-ssh` 通过 manager 受控 upsert runtime Secret,list/show 只展示 SecretRef、key presence 和 hash suffix;Secret value 不进入输出。 | | redaction 最小规则 | 已实现主路径 | Secret dry-run 工具、event、Job dry-run 输出、self-test 和真实主闭环均不打印 Secret value;复杂审计按 [spec-v01-validation.md](spec-v01-validation.md) 人工抽查。 | | 外部 secret manager | 未采用 | 如需 Vault/ExternalSecrets/SOPS,后续单独更新规格。 |