Skip to content

[Feature Request] SecretRef 凭据管理:引用配置、受控物化与安全的 API Key 管理交互 #397

Description

@edabchann

背景

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 约束

建议只接受固定、不可执行的逻辑标识:

ccs-secret://v1/<name>

其中 <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,只显示别名、引用和状态,例如:

API Key
已配置 · UCAS
[管理…]

其中建议分离:

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。

点击“管理…”后可以显示受控操作:

  1. 修改 Key 名:只更新 display_name,不读取 key;
  2. 替换 Key 值:提供空的密码输入框,绝不回填旧值;后端写入 secret backend 后只返回成功状态;
  3. 查看 Key 值:作为高敏感、显式动作。先进行风险确认,再短暂显示真实值,提供复制按钮及 Ctrl/Cmd+C 提示;切换页面、失焦或超时后自动隐藏;
  4. 移除 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 的显示仅限用户明确确认后的短暂窗口。

可分阶段实现

  1. SecretRef 模型、credential binding、受限 file backend;canonical DB / export / backup / sync 去除明文;
  2. 先适配一个 provider 路径(例如 Codex),在 apply 时解析并写入 live config;
  3. 修改 provider 编辑、复制、import-live、stream-check/speedtest 与 IPC,使前端默认不接触 secret value;
  4. 接入系统 keychain/secret-service;
  5. 可选的加密 secret bundle、MCP credential injection 或本地 credential broker。

如果维护者认可该方向,我愿意根据当前代码结构继续细化数据迁移、adapter 接口与测试用例建议。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions