> ## Documentation Index
> Fetch the complete documentation index at: https://gomodel.enterpilot.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Secret 引用

> 使用 ${env:...} 和 ${file:...} 引用，从环境变量或挂载的文件中读取 API 密钥、DSN 及其他凭据。

Secret 引用告诉 GoModel 从哪里读取某个值，而不是直接保存该值本身：

```yaml theme={null}
server:
  master_key: ${file:/run/secrets/gomodel-master-key}

providers:
  openai:
    type: openai
    api_key: ${env:OPENAI_KEY}
```

引用可用于 `config.yaml` 中的任何字符串设置、用于设置这些配置项的环境变量，以及提供商环境变量：

```bash theme={null}
OPENAI_API_KEY='${file:/run/secrets/openai}'
POSTGRES_URL='${file:/run/secrets/postgres-url}'
```

在 shell 中请用单引号包裹该值，以免 shell 对其进行展开；`.env` 文件无需加引号。Docker Compose 会对其自身文件中的 `${...}` 进行插值，因此在其中请写成 `$${file:...}`；Compose 会将 `${file:...}` 传递给 GoModel。

## 语法

引用的格式为 `${<scheme>:<reference>}`。它可以嵌在更长的值中，一个值也可以包含多个引用：

```yaml theme={null}
providers:
  openai:
    type: openai
    api_key: ${env:OPENAI_KEY}
    proxy_url: http://gomodel:${file:/run/secrets/proxy-pass}@proxy:3128

mcp:
  servers:
    github:
      url: https://mcp.example.com/mcp
      headers:
        Authorization: Bearer ${env:GITHUB_TOKEN}
```

解析后的值会按原样使用。它可以包含换行符、引号或任何其他字节，因此服务账号 JSON 文件也可以使用，并且解析后的值不会再被扫描以查找其他引用。

## 方案

| 方案 | 引用 | 值 |
| - | - | - |
| `env` | 变量名 | 该变量的值；未设置或为空时启动终止 |
| `file` | 绝对路径 | 文件内容，并去掉末尾的一个换行符；最大 1 MiB；文件不存在或为空时启动终止 |

`file` 可直接读取 Docker 和 Kubernetes 的 Secret 挂载，因此无需入口脚本将其复制到环境变量中。该路径必须是常规文件或指向常规文件的符号链接（这些挂载正是如此）；FIFO 或设备文件会被拒绝，因为 GoModel 在每次重新加载时都会再次读取该文件。

`env` 与普通的 `${NAME}` 形式有两点不同：它是严格的，并且可以在环境变量中使用，而不仅限于 `config.yaml`。普通形式的行为不变：`${NAME}` 和 `${NAME:-default}` 会在 `config.yaml` 中展开，未设置的 `${NAME}` 会保持原样，这会导致以它作为密钥的提供商被丢弃。

外部 Secret 管理器（HashiCorp Vault、AWS Secrets Manager、GCP Secret Manager、Azure Key Vault）可以通过 [GoModel Pro](https://gomodel.enterpilot.io/docs/zh/pro/overview.md) 作为额外的方案接入。

## 解析失败会终止启动

无法解析的引用会使网关终止，错误信息会指明配置项和方案，但绝不会包含值本身：

```text theme={null}
failed to resolve secret references: providers.openai.api_key: secret reference ${file:...}: open /run/secrets/openai: no such file or directory
```

通过环境变量设置的提供商 API 密钥会以该变量名报告，例如 `OPENAI_API_KEY_2: secret reference ${file:...}`。GoModel 不认识的方案不会在错误中重复显示，因为它是取自值本身的文本；请检查所指明的配置项是否有拼写错误。

[重新加载](https://gomodel.enterpilot.io/docs/zh/advanced/cli.md#配置重新加载)失败时，会保留正在运行的配置。GoModel 绝不会带着未解析的引用启动，也不会将其发送到上游。

每个引用有 30 秒的解析时间。耗时更长的引用（例如位于卡住的网络挂载上的文件）会以 `context deadline exceeded` 失败，而不会拖住启动、重新加载或控制台保存操作。

只有实际使用的值才会被解析：被环境变量覆盖的 `config.yaml` 提供商值、GoModel 忽略的提供商环境变量（例如存在多个 `openai` 提供商且没有一个命名为 `openai` 时的 `OPENAI_MODELS`），或因缺少凭据而被跳过的提供商，都不会被查找。

## 轮换

GoModel 不会轮询 `env` 或 `file` 引用。轮换某个引用后，请重新加载：`gomodel --reload` 或 `SIGHUP`。

监视 Secret 管理器的扩展（例如 GoModel Pro 的 vault）会在值发生变化时通知 GoModel。随后 GoModel 会重新解析所有引用，并且：

* 当仅提供商 API 密钥发生变化时，原地替换这些密钥。进行中的请求会使用其开始时的密钥完成。绑定到已移除密钥的会话会转移到另一个密钥；其他大多数会话保持原有密钥。
* 当 `config.yaml` 或环境中的任何其他内容发生变化时（DSN、主密钥、代理 URL、服务账号 JSON），像 `SIGHUP` 一样重新加载配置。重新加载失败时会保留正在运行的配置，并在下一次变化时重试。通过控制台保存的引用永远不会触发配置重新加载；参阅[控制台管理的凭据](#控制台管理的凭据)。
* 当某个引用无法重新解析时，保留所有当前值，并记录一条指明该配置项的警告日志。

日志行会指明发生变化的配置项，但绝不会包含其值：

```text theme={null}
rotated provider API keys in place providers=[openai] fields=[providers.openai.api_keys[1]]
```

更改语义缓存嵌入器所用提供商的密钥也会触发重新加载，因为嵌入器保存了自己的一份密钥副本。

## 控制台管理的凭据

通过控制台或 Admin API 保存的提供商凭据、MCP 服务器请求头和护栏 Secret 字段同样接受引用，语法相同：

| 实体 | 字段 |
| - | - |
| 提供商凭据 | API 密钥、服务账号 JSON 和 base64 JSON、代理 URL |
| MCP 服务器 | 请求头值 |
| 护栏 | 插件的 Secret 字段 |

* 引用按输入原样存储；GoModel 在构建提供商、MCP 连接或护栏时解析它。
* 引用本身不是 Secret，因此对于仅由引用组成的值，控制台和 Admin API 会以不掩码的方式显示；MCP 请求头还可以以认证方案开头（`Bearer ${env:TOKEN}`），当代理 URL 的密码是引用时，也会显示该代理 URL。字面量 Secret 以及混合了字面文本和引用的值仍会被掩码；将掩码值原样发回会保留已存储的值。
* 无法解析的引用会在保存时被拒绝，并返回指明字段和方案的 `400`。
* 只有主密钥可以添加或更改引用。引用在网关主机上解析，因此它可以读取网关能够读取的任何环境变量或文件，包括主密钥。任何其他凭据（例如具有控制台访问权限的托管密钥）都会收到指明该字段的 `403`（`secret_reference_requires_master_key`）。它们仍然可以保存字面量 Secret、发回掩码值，以及重新保存引用未改变的实体。未配置主密钥时，控制台凭据像以前一样保存引用。
* 出于同样的原因，只有主密钥可以更改持有引用的实体将其发送到的目标：MCP 服务器的 `url`，提供商的 `type`、`base_url`、`backend` 或 `proxy_url`，或护栏的类型或服务 URL（例如 Presidio 的 `analyzer_url`）。其他凭据会收到同样指明该字段的 `403`，但仍可编辑其他所有字段。
* 每次解析器调用都会携带一个用于审计的字段路径，例如 `provider_credentials.openai.api_keys[0]`、`mcp_servers.github.headers.Authorization` 或 `guardrail_definitions.pii.config.api_key`。
* 当被引用的值[轮换](#轮换)时，只会重新安装受影响的实体：提供商的密钥会被原地替换，提供商的其他任何变化会重建该提供商，MCP 服务器会重新连接，护栏会被重建。如果引用无法解析，该实体保留其当前值。

在 `config.yaml` 中声明的护栏规则也以同样方式存储：插件 Secret 字段中的引用按书写内容保存，并在构建护栏时解析，因此数据库中永远不会保存 Secret 本身。

如果已存储的引用在启动时无法解析，相应的提供商凭据或 MCP 服务器会被记录日志并跳过，网关的其余部分照常启动。在重新加载时，正在提供服务的提供商凭据或 MCP 服务器绝不会因此被丢弃，也不会因为某个密钥现在解析为空值而被丢弃：重新加载会失败并指明该实体和字段，正在运行的配置（包括该实体）会继续提供服务。请修复或删除该实体，然后再次重新加载。已经被跳过的实体会再次被跳过，不会阻止重新加载。
护栏作为一个整体加载，并采用失败即拒绝（fail closed）策略：启动失败；重新加载时保留正在运行的配置；定期刷新时保留现有的护栏。

在引用功能出现之前保存的值也以同样方式读取，因此包含 `${scheme:...}` 的已存储字面量现在会被视为引用。请使用 `$${` 重新保存该值，以使其保持为字面量。

扩展可以注册一个 Secret 写入器。此后，在控制台中输入的字面量会被存储到 Secret 管理器中，只保存其引用；当对应的实体或字段被删除时，写入器的 Secret 也会被删除。如果某次保存仍持有一个其实体已不再存储的写入引用（例如在他人替换该 Secret 之前打开的表单），该保存会被拒绝：请重新加载后再保存。GoModel 核心版本不附带写入器。

## 字面量 `${`

要表示字面量 `${`，请写成 `$${`。`pa$${x}` 会变成 `pa${x}`。

## 限制

* `env` 和 `file` 引用在加载或重新加载配置时解析；参阅[轮换](#轮换)。
* 它们适用于字符串设置。布尔值或数字设置不能是引用；如果 GoModel 解析为数字、布尔值、时长或列表的环境变量（`LOGGING_ENABLED`、`HTTP_TIMEOUT`、`ENABLED_PASSTHROUGH_PROVIDERS` 等）中包含引用，启动会终止。JSON 变量在其字符串值中接受引用。
* 提供商环境变量是例外：`OPENAI_MODELS`、`OPENAI_MODEL_FILTER_*` 和 `OPENAI_SESSION_STICKY_KEYS` 会在解析其格式之前先解析引用。
* 在控制台管理的实体中，只有[控制台管理的凭据](#控制台管理的凭据)中列出的字段接受引用。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.