密钥管理
OpenClaw 支持添加性密钥引用,因此凭据不需要以明文形式存储在配置文件中?
明文仍然有效。密钥引用是可选的?
目标和运行时模型
密钥解析为内存中的运行时快照?
- 解析在激活时是急切的,而不是在请求路径上延迟的?
- 如果任何引用的凭据无法解析,启动将快速失败?
- 重新加载使用原子交换:完全成功或保持最后已知良好?
- 运行时请求从活动的内存快照中读取?
这使密钥提供商故障远离热请求路径?
入职引用预检
当入职以交互模式运行且您选择密钥引用存储时,OpenClaw 在保存前执行快速预检查:
- 环境引用:验证环境变量名称并确认入职期间可见非空值?
- 提供商引用(
file?exec):验证选定的提供商,解析提供的id,并检查值类型?
如果验证失败,入职显示错误并让您重试?
SecretRef 合约
到处使用一个对象形状:
{ source: "env" | "file" | "exec", provider: "default", id: "..." }
source: "env"
{ source: "env", provider: "default", id: "OPENAI_API_KEY" }
验证?
provider必须匹配^[a-z][a-z0-9_-]{0,63}$id必须匹配^[A-Z][A-Z0-9_]{0,127}$
source: "file"
{ source: "file", provider: "filemain", id: "/providers/openai/apiKey" }
验证?
provider必须匹配^[a-z][a-z0-9_-]{0,63}$id必须是绝?JSON 指针(/...?- 段中?RFC6901 转义:
~=>~0,/=>~1
source: "exec"
{ source: "exec", provider: "vault", id: "providers/openai/apiKey" }
验证?
provider必须匹配^[a-z][a-z0-9_-]{0,63}$id必须匹配^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$
提供商配?
?secrets.providers 下定义提供商?
{
secrets: {
providers: {
default: { source: "env" },
filemain: {
source: "file",
path: "~/.openclaw/secrets.json",
mode: "json", // ?"singleValue"
},
vault: {
source: "exec",
command: "/usr/local/bin/openclaw-vault-resolver",
args: ["--profile", "prod"],
passEnv: ["PATH", "VAULT_ADDR"],
jsonOnly: true,
},
},
defaults: {
env: "default",
file: "filemain",
exec: "vault",
},
resolution: {
maxProviderConcurrency: 4,
maxRefsPerProvider: 512,
maxBatchBytes: 262144,
},
},
}
环境提供?
- 可选通过
allowlist的允许列表? - 缺失/空环境值会导致解析失败?
文件提供?
- ?
path读取本地文件? mode: "json"期望 JSON 对象负载并将id解析为指针?mode: "singleValue"期望引用 id"value"并返回文件内容?- 路径必须通过所有权/权限检查?
Exec 提供?
- 运行配置的非 symlink 绝对二进制路径?
- 默认情况下,
command必须指向常规文件(不?symlink)? - 设置
allowSymlinkCommand: true以允?symlink 命令路径(例?Homebrew shims)。OpenClaw 验证解析的目标路径? - 仅在受信任的包管理器路径需要时启用
allowSymlinkCommand,并将其?trustedDirs配对(例?["/opt/homebrew"])? - 当设?
trustedDirs时,检查应用于解析的目标路径? - 支持超时、无输出超时、输出字节限制、环境允许列表和受信任目录?
- 请求负载(stdin):
{ "protocolVersion": 1, "provider": "vault", "ids": ["providers/openai/apiKey"] }
- 响应负载(stdout):
{ "protocolVersion": 1, "values": { "providers/openai/apiKey": "sk-..." } }
可选每?ID 错误?
{
"protocolVersion": 1,
"values": {},
"errors": { "providers/openai/apiKey": { "message": "not found" } }
}
Exec 集成示例
1Password CLI
{
secrets: {
providers: {
onepassword_openai: {
source: "exec",
command: "/opt/homebrew/bin/op",
allowSymlinkCommand: true, // 需?Homebrew 符号链接二进制文?
trustedDirs: ["/opt/homebrew"],
args: ["read", "op://Personal/OpenClaw QA API Key/password"],
passEnv: ["HOME"],
jsonOnly: false,
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [{ id: "gpt-5", name: "gpt-5" }],
apiKey: { source: "exec", provider: "onepassword_openai", id: "value" },
},
},
},
}
HashiCorp Vault CLI
{
secrets: {
providers: {
vault_openai: {
source: "exec",
command: "/opt/homebrew/bin/vault",
allowSymlinkCommand: true, // 需?Homebrew 符号链接二进制文?
trustedDirs: ["/opt/homebrew"],
args: ["kv", "get", "-field=OPENAI_API_KEY", "secret/openclaw"],
passEnv: ["VAULT_ADDR", "VAULT_TOKEN"],
jsonOnly: false,
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [{ id: "gpt-5", name: "gpt-5" }],
apiKey: { source: "exec", provider: "vault_openai", id: "value" },
},
},
},
}
sops
{
secrets: {
providers: {
sops_openai: {
source: "exec",
command: "/opt/homebrew/bin/sops",
allowSymlinkCommand: true, // 需?Homebrew 符号链接二进制文?
trustedDirs: ["/opt/homebrew"],
args: ["-d", "--extract", '["providers"]["openai"]["apiKey"]', "/path/to/secrets.enc.json"],
passEnv: ["SOPS_AGE_KEY_FILE"],
jsonOnly: false,
},
},
},
models: {
providers: {
openai: {
baseUrl: "https://api.openai.com/v1",
models: [{ id: "gpt-5", name: "gpt-5" }],
apiKey: { source: "exec", provider: "sops_openai", id: "value" },
},
},
},
}
范围内字段(v1?
~/.openclaw/openclaw.json
models.providers.<provider>.apiKeyskills.entries.<skillKey>.apiKeychannels.googlechat.serviceAccountchannels.googlechat.serviceAccountRefchannels.googlechat.accounts.<accountId>.serviceAccountchannels.googlechat.accounts.<accountId>.serviceAccountRef
~/.openclaw/agents/<agentId>/agent/auth-profiles.json
profiles.<profileId>.keyRef用于type: "api_key"profiles.<profileId>.tokenRef用于type: "token"
OAuth 凭据存储更改不在范围内?
必需行为和优先级
- 无引用的字段:不变?
- 带引用的字段:在激活时需要?
- 如果明文和引用都存在,引用在运行时获胜,明文被忽略?
警告代码?
SECRETS_REF_OVERRIDES_PLAINTEXT
激活触发器
密钥激活在以下情况下尝试:
- 启动(预检加最终激活)
- 配置重载热应用路?
- 配置重载重启检查路?
- 通过
secrets.reload手动重载
激活合约:
- 成功原子交换快照?
- 启动失败中止 Gateway 启动?
- 运行时重载失败保持最后已知良好快照?
降级和恢复的 operator 信号
当重载时激活在健康状态后失败时,OpenClaw 进入降级密钥状态?
一次性系统事件和日志代码?
SECRETS_RELOADER_DEGRADEDSECRETS_RELOADER_RECOVERED
行为?
- 降级:运行时保持最后已知良好快照?
- 恢复:成功激活后发出一次?
- 已经在降级时重复失败记录警告但不发送事件?
- 启动快速失败不发出降级事件,因为尚不存在运行时快照?
审计和配置工作流
使用此默?operator 流程?
openclaw secrets audit --check
openclaw secrets configure
openclaw secrets audit --check
迁移完整性:
- 当这些技能使?API 密钥时,包含
skills.entries.<skillKey>.apiKey目标? - 如果
audit --check在部分迁移后仍报告明文发现,迁移剩余报告的路径并重新运行审计?
secrets audit
发现包括?
- 静态明文值(
openclaw.json、auth-profiles.json、.env? - 未解析的引用
- 优先级阴影(
auth-profiles优先于配置引用) - 旧版残留(
auth.json、OAuth 超出范围提醒?
secrets configure
交互式助手:
- 首先配置
secrets.providers(env/file/exec、添?编辑/删除? - 让您选择
openclaw.json中的密钥字段 - 捕获 SecretRef 详情(
source、provider、id? - 运行预检解析
- 可以立即应用
有用的模式:
openclaw secrets configure --providers-onlyopenclaw secrets configure --skip-provider-setup
configure 应用默认?
- 从目标提供商?
auth-profiles.json中清理匹配的静态凭? - ?
auth.json中清理旧版静?api_key条目 - ?
<config-dir>/.env中清理匹配的已知密钥?
secrets apply
应用保存的计划:
openclaw secrets apply --from /tmp/openclaw-secrets-plan.json
openclaw secrets apply --from /tmp/openclaw-secrets-plan.json --dry-run
有关严格目标/路径合约详情和确切拒绝规则,请参阅:
单向安全策略
OpenClaw 有意不写包含迁移前明文密钥值的回滚备份?
安全模型?
- 预检必须在写入模式之前成?
- 运行时激活在提交前验?
- 应用使用原子文件替换更新文件,并在失败时尽力恢复内存
auth.json 兼容性说?
对于静态凭据,OpenClaw 运行时不再依赖明?auth.json?
- 运行时凭据来源是解析的内存快照?
- 当发现时,旧?
auth.json静?api_key条目被清理? - OAuth 相关旧版兼容性行为保持分离?
相关文档
- CLI 命令:secrets
- 计划合约详情:Secrets Apply Plan Contract
- 认证设置:Authentication
- 安全态势:Security
- 环境优先级:Environment Variables