背景
CC Switch 当前将 provider 的公开配置(endpoint、协议、模型能力等)与认证材料一起管理,并会向目标 CLI 的 live 配置写入认证信息。对于 Claude / Codex / Gemini / OpenCode 等客户端,最终 live 配置中存在明文认证材料有时不可避免;但这不意味着 CC Switch 自身的 canonical 数据库、普通导出、自动备份和同步载荷也必须保留明文 API Key。
这在 AI 辅助编辑、复制 provider、配置导出/备份/同步时会扩大密钥传播面。
相关场景:
提议:SecretRef + canonical/live 分离
将 CC Switch 管理的规范(canonical)配置与目标工具的运行时(live)配置分离:
Canonical provider config
endpoint / protocol / models / capabilities / SecretRef
└─ 不含真实 API Key
apply / switch / stream-check / speedtest
└─ 在 CC Switch 后端受控地解析 SecretRef
└─ 只在目标客户端确有需要时物化真实值到 live config
例如 canonical 配置只包含:
{
"id": "oyster",
"name": "Oyster",
"baseUrl": "https://provider.example/v1",
"protocol": "openai-responses",
"credentialRef": "ccs-secret://v1/oyster-api-key"
}
SecretRef 约束
建议只接受固定、不可执行的逻辑标识:
其中 <name> 使用受限字符集,例如 [a-z0-9][a-z0-9-]{0,63}。
不应支持:
file:///some/path
!cat /some/path
${ARBITRARY_ENV_VAR}
$(command)
这样可以避免 provider 配置成为任意路径读取、环境继承或命令执行入口。SecretRef 是逻辑 ID,不应泄露实际后端路径。
建议的存储与同步语义
- provider 的公开配置和 credential binding 保存
SecretRef,而不保存 secret value;
- 普通
config show/export/backup、自动备份、WebDAV/S3 同步仅包含公开配置和 SecretRef;
- secret value 默认不同步;新设备可显示 provider,但标记为 credential unavailable,要求用户显式设置;
- 未来若需要跨设备迁移,应提供单独、显式启用、加密的 secret bundle,而不是让普通同步自动携带凭据;
- 对已经受 CC Switch 管理的 provider,live config 的反向导入不得将明文 Key 回灌 canonical 存储;未知 live config 中的明文 Key 必须走显式“导入到 vault”流程。
首期 backend 可以是 CC Switch 配置根目录下的严格权限 file vault;后续可扩展 macOS Keychain、Windows Credential Manager 和 Linux Secret Service。切换 backend 不应改变 SecretRef。
API Key 的人机交互
普通 provider 编辑页不显示真实 API Key,只显示别名、引用和状态,例如:
其中建议分离:
secret_id: 稳定内部 ID,例如 ucas-api-key
secret_ref: ccs-secret://v1/ucas-api-key
display_name: 用户可见别名,例如 UCAS
secret_value: 仅存在于 secret backend
display_name 不是物理路径或 secret ID;重命名不改变 Key 值、SecretRef 或 target live config。
点击“管理…”后可以显示受控操作:
- 修改 Key 名:只更新
display_name,不读取 key;
- 替换 Key 值:提供空的密码输入框,绝不回填旧值;后端写入 secret backend 后只返回成功状态;
- 查看 Key 值:作为高敏感、显式动作。先进行风险确认,再短暂显示真实值,提供复制按钮及
Ctrl/Cmd+C 提示;切换页面、失焦或超时后自动隐藏;
- 移除 Key。
查看真实值时,普通配置读取接口仍不可返回 key。建议设计成动作导向的后端接口,例如:
list_provider_credentials(provider_id)
rename_credential(secret_id, display_name)
replace_credential_value(secret_id, new_value)
reveal_credential_value(secret_id, explicit_confirmation)
remove_credential(secret_id)
普通 provider 查询只返回:
{
"secretRef": "ccs-secret://v1/ucas-api-key",
"displayName": "UCAS",
"status": "configured",
"canReveal": true
}
只有 reveal 的显式调用才返回值,并且不得写入日志、provider store、持久化前端状态、诊断报告或后续常规 provider 响应。未来某些 backend 可以支持 configured + usable + not revealable,UI 不应假定每个已配置 secret 都可导出。
后端调用边界
除 apply/switch 外,余额查询、测速、用量脚本等也不应让前端携带 api_key 参数。建议调用者只传 provider_id 或受控 credential binding,由后端解析 SecretRef 并在内存中发起请求。
这同样避免真实值通过 CLI/Tauri IPC、UI 状态、表单回填、错误日志或开发者工具重复暴露。
安全边界
该提议不承诺防御拥有同一 OS 用户权限、能读取 live 配置或任意执行代码的恶意进程。部分目标客户端不得不保存明文 live config 的现实也不会消失。
目标是缩小更常见的扩散面:AI 参与配置管理、provider 复制、普通编辑、普通导出、数据库备份和同步。真实 key 的显示仅限用户明确确认后的短暂窗口。
可分阶段实现
SecretRef 模型、credential binding、受限 file backend;canonical DB / export / backup / sync 去除明文;
- 先适配一个 provider 路径(例如 Codex),在 apply 时解析并写入 live config;
- 修改 provider 编辑、复制、import-live、stream-check/speedtest 与 IPC,使前端默认不接触 secret value;
- 接入系统 keychain/secret-service;
- 可选的加密 secret bundle、MCP credential injection 或本地 credential broker。
如果维护者认可该方向,我愿意根据当前代码结构继续细化数据迁移、adapter 接口与测试用例建议。
背景
CC Switch 当前将 provider 的公开配置(endpoint、协议、模型能力等)与认证材料一起管理,并会向目标 CLI 的 live 配置写入认证信息。对于 Claude / Codex / Gemini / OpenCode 等客户端,最终 live 配置中存在明文认证材料有时不可避免;但这不意味着 CC Switch 自身的 canonical 数据库、普通导出、自动备份和同步载荷也必须保留明文 API Key。
这在 AI 辅助编辑、复制 provider、配置导出/备份/同步时会扩大密钥传播面。
相关场景:
提议:SecretRef + canonical/live 分离
将 CC Switch 管理的规范(canonical)配置与目标工具的运行时(live)配置分离:
例如 canonical 配置只包含:
{ "id": "oyster", "name": "Oyster", "baseUrl": "https://provider.example/v1", "protocol": "openai-responses", "credentialRef": "ccs-secret://v1/oyster-api-key" }SecretRef 约束
建议只接受固定、不可执行的逻辑标识:
其中
<name>使用受限字符集,例如[a-z0-9][a-z0-9-]{0,63}。不应支持:
这样可以避免 provider 配置成为任意路径读取、环境继承或命令执行入口。
SecretRef是逻辑 ID,不应泄露实际后端路径。建议的存储与同步语义
SecretRef,而不保存 secret value;config show/export/backup、自动备份、WebDAV/S3 同步仅包含公开配置和 SecretRef;首期 backend 可以是 CC Switch 配置根目录下的严格权限 file vault;后续可扩展 macOS Keychain、Windows Credential Manager 和 Linux Secret Service。切换 backend 不应改变
SecretRef。API Key 的人机交互
普通 provider 编辑页不显示真实 API Key,只显示别名、引用和状态,例如:
其中建议分离:
display_name不是物理路径或 secret ID;重命名不改变 Key 值、SecretRef 或 target live config。点击“管理…”后可以显示受控操作:
display_name,不读取 key;Ctrl/Cmd+C提示;切换页面、失焦或超时后自动隐藏;查看真实值时,普通配置读取接口仍不可返回 key。建议设计成动作导向的后端接口,例如:
普通 provider 查询只返回:
{ "secretRef": "ccs-secret://v1/ucas-api-key", "displayName": "UCAS", "status": "configured", "canReveal": true }只有
reveal的显式调用才返回值,并且不得写入日志、provider store、持久化前端状态、诊断报告或后续常规 provider 响应。未来某些 backend 可以支持configured + usable + not revealable,UI 不应假定每个已配置 secret 都可导出。后端调用边界
除 apply/switch 外,余额查询、测速、用量脚本等也不应让前端携带
api_key参数。建议调用者只传provider_id或受控 credential binding,由后端解析 SecretRef 并在内存中发起请求。这同样避免真实值通过 CLI/Tauri IPC、UI 状态、表单回填、错误日志或开发者工具重复暴露。
安全边界
该提议不承诺防御拥有同一 OS 用户权限、能读取 live 配置或任意执行代码的恶意进程。部分目标客户端不得不保存明文 live config 的现实也不会消失。
目标是缩小更常见的扩散面:AI 参与配置管理、provider 复制、普通编辑、普通导出、数据库备份和同步。真实 key 的显示仅限用户明确确认后的短暂窗口。
可分阶段实现
SecretRef模型、credential binding、受限 file backend;canonical DB / export / backup / sync 去除明文;如果维护者认可该方向,我愿意根据当前代码结构继续细化数据迁移、adapter 接口与测试用例建议。