docs(provider): 记录本地密钥加密边界

This commit is contained in:
2026-08-30 10:57:14 +08:00
parent 352975d753
commit 0bb78e004b
3 changed files with 17 additions and 16 deletions
@@ -67,11 +67,21 @@ Provider Adapter 的错误在 FastAPI 路由转换为统一 API Error
## 3. 凭据边界
设置页只保存 `credential_id`。桌面 Host 负责将 Stronghold 中的密钥以临时凭据上下文注入 AI Core`EnvironmentCredentialResolver` 根据 Credential ID 解析。API Key 明文不会Provider Contract、PinialocalStorage、日志或本文档
设置页选择 OpenAI 或 DeepSeek 预设后展示密码类型的 API Key 输入框,不再要求用户理解 Credential ID。输入值只存在于表单的临时 `ref`不会入 PinialocalStorage;请求完成、取消表单或失败后都会清空
当前尚未实现 Tauri Host 的开发环境中,`openai``deepseek` Credential ID 分别兼容标准的 `OPENAI_API_KEY``DEEPSEEK_API_KEY` 进程环境变量。Host 注入的 `AINOTE_CREDENTIAL_OPENAI``AINOTE_CREDENTIAL_DEEPSEEK` 优先级更高。环境变量必须在启动 AI Core 前设置;Provider 明确指定了 Credential ID 但进程内无法解析时,Adapter 会在网络请求前返回 `PROVIDER_CREDENTIAL_MISSING`,避免把本地配置缺失误报为厂商 401。
API Key 通过独立接口写入:
本次没有创建或使用本机 OpenAI API Key,也没有对 OpenAI、DeepSeek 发起真实请求。外部模型列表的联调需要在桌面 Host 完成 Stronghold 注入后进行。
```http
GET /api/credentials/{credential_id}
PUT /api/credentials/{credential_id}
DELETE /api/credentials/{credential_id}
```
PUT 请求使用 Pydantic `SecretStr` 接收密钥,响应仅包含 Credential ID 和 `configured` 状态。后端使用 Fernet 认证加密,将密文保存到 `data/credentials/credentials.json`,主密钥保存到 `data/credentials/master.key`;目录和文件尽可能设置为仅当前用户可访问并整体排除版本控制。写入采用临时文件替换,避免进程中断留下半写文件。Provider 发起请求时按 Credential ID 解密,解密失败转换为统一 Provider Error,任何读取接口均不返回明文。
本地开发存储的主密钥与密文仍位于同一用户数据目录,因此它解决的是仓库泄漏、普通配置误提交和静态明文暴露,不等同于操作系统安全硬件或 Stronghold。Tauri 集成后应以 Stronghold 实现替换 `EncryptedCredentialStore`。无界面环境仍兼容 `OPENAI_API_KEY``DEEPSEEK_API_KEY` 和 Host 注入的 `AINOTE_CREDENTIAL_<ID>`;设置页保存的本地密钥优先,环境变量仅作为回退。
自动化测试仅使用虚构测试值,验证磁盘文件不包含明文、加解密往返、API 响应不泄密,以及 Provider 能用解密后的值构造 Authorization Header。本次没有使用真实 OpenAI 或 DeepSeek Key,也没有向厂商发起真实请求。
## 4. 验证