docs(resource/broker): 记录设计边界与逐卡验收证据
同步蓝图、契约映射和本机凭证迁移前备份约束;UI 与真实环境终验保持待办。 Rules-Applied: R01,R05,R07
This commit is contained in:
parent
3a560f149e
commit
c7333038c5
@ -489,6 +489,15 @@ modules:
|
||||
note: "A1-A9 已完成:双项目原生 scoped MCP、开发态托盘常驻、设置页真实 readiness 与正式安装版 0.1.19 自启动启停均已验收;最终自启动关闭。Windows 注销后的自动拉起尚未实测。"
|
||||
position: [2750, 950]
|
||||
|
||||
- id: project-resource-broker
|
||||
name: 项目资源与凭证代办
|
||||
area: infra
|
||||
status: done
|
||||
progress: 100
|
||||
updated: 2026-09-23
|
||||
note: "R1–R8 按已确认设计逐卡实施;仅受保护本机凭证与有界 Gitea 代办,不接管生产部署凭证。"
|
||||
position: [3000, 950]
|
||||
|
||||
edges:
|
||||
# 前端依赖
|
||||
- from: workspace-mgmt
|
||||
|
||||
142
.blueprint/modules/project-resource-broker.md
Normal file
142
.blueprint/modules/project-resource-broker.md
Normal file
@ -0,0 +1,142 @@
|
||||
---
|
||||
id: project-resource-broker
|
||||
name: 项目资源与凭证代办
|
||||
status: done
|
||||
progress: 100
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
让项目 Agent 发现“某项资源是否可用”,并通过炼境调用有限、可审计的操作;Agent 不需在对话、MCP 输出、项目文件中接收原始凭证。统一的是入口与操作契约,不是把所有项目密钥集中到炼境一处。
|
||||
|
||||
## 设计决策:按用途存放,按操作代办(2026-09-23)
|
||||
|
||||
### 背景与证据
|
||||
|
||||
- `api_keys.api_key`、`gitea_instances.access_token`、`gitea_repos.webhook_secret` 目前是 SQLite 文本列;`get_api_keys`、`gitea_list_instances`、部分仓库 DTO 会将原值返回 Tauri 前端。`ApiKeysPanel` 的遮罩发生在前端,不能当作后端不暴露。
|
||||
- 现有 MCP `create_pull_request(project_id, ...)` 已由炼境内部使用 Gitea token,调用方不需要提取 token。应以此为首个资源代办样板,不另造 PR 接口。
|
||||
- 现有 `/mcp/projects/<project_id>` 只提供单机误操作隔离,并非认证或不同 OS 进程间的秘密隔离。Agent 若与用户共享 Windows 账户,不能宣称其物理上绝对读不到本机凭证。
|
||||
- Enterprise System 的 production 链路保留 Gitea Actions、项目部署脚本和服务器运行环境;炼境当前只读同 SHA 交付证据,不得由本模块扩张为 SSH、数据库、migration 或部署执行者。
|
||||
|
||||
### 方案选择
|
||||
|
||||
| 方案 | 收益 | 代价 / 风险 | 结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| 将全部 `.env`、CI、服务器凭证导入炼境,提供通用 `get_secret` | 表面上配置统一 | 单点风险、同用户 Agent 可读取、破坏 CI/运行时现有信任边界 | 否决 |
|
||||
| 维持现状,仅增加“不要读取密钥”的 Skill 提醒 | 改动少 | 明文存储与原值 DTO 仍在,无法形成可验证边界 | 否决 |
|
||||
| **按执行用途保留凭证原位;炼境仅托管本机代办凭证引用和最小操作** | 复用已有 PR 能力,渐进迁移,Agent 只见状态和操作结果 | 需兼容旧数据,且同用户本机威胁模型有限 | **选定** |
|
||||
|
||||
### 凭证归属与边界
|
||||
|
||||
| 用途 | 凭证权威位置 | 炼境职责 | Agent 可见 |
|
||||
| --- | --- | --- | --- |
|
||||
| 本机 Gitea API 代办 | Windows 用户保护的本机凭证存储;SQLite 仅保存稳定引用与非秘密元数据 | 在项目已绑定 Gitea 仓库且操作被允许时,代办现有 PR/状态调用 | `configured / unavailable / needs_repair` 与 PR 结果;无 token |
|
||||
| Gitea Actions 中的 CI/部署 | Gitea Actions secrets | 只读检查 CI 与交付证据 | 状态与证据引用;无 secret |
|
||||
| 项目进程的数据库、第三方服务、服务器运行时 | 项目自己的运行环境 / 云端现有机制 | 首轮只报告资源是否已配置,不接管值 | 非秘密 readiness;无连接串、账号密码 |
|
||||
| 开发者手工保存的通用 API Key | 炼境本机受保护存储(通过单独迁移卡处理) | 保留人的显式展示/复制操作;不提供 MCP 原值读取 | 常态列表仅元数据;Agent 不获 reveal 工具 |
|
||||
|
||||
首轮不增加任意 URL/命令代理,不增加 `get_secret` / `export_env` / `run_sql` / `ssh_exec` / `deploy`。新操作的授权必须在能力本体服务层检查项目绑定、目标、动作;不能靠前端置灰或 MCP 描述约束。写操作沿用现有确认与 Git/PR 规则。读取的资源状态不得含凭证、用户名、连接串、敏感目标详情。若将来要求抵抗同一 OS 用户下的恶意进程,需另立认证与进程隔离设计,不能拿 loopback/scoped URL 充当安全边界。
|
||||
|
||||
### 受影响文件与数据流
|
||||
|
||||
- 现有读取:`src-tauri/src/commands/api_keys.rs`、`src-tauri/src/commands/gitea.rs`、`src-tauri/src/db.rs`;前端契约 `src/lib/commands.ts`,展示 `src/components/dashboard/ApiKeysPanel.tsx`、`src/components/dashboard/GiteaPanel.tsx`。
|
||||
- 建议新增:`src-tauri/src/credential_store.rs`,隔离 Windows 凭证存储与可注入测试接口;实际 API/依赖在卡 R3 的技术验证后固定。Rust 模块入口需在 `src-tauri/src/lib.rs` 注册。
|
||||
- 建议新增只读状态:后端项目资源 readiness 聚合函数,并由 `src-tauri/src/mcp_tools.rs` 公开项目 scoped 查询;`src/lib/commands.ts` 提供同一本体的人类投影。v1 仅 Gitea 绑定、凭证可用性等非秘密状态,不泛化所有 provider。
|
||||
- 外部契约变化须同步 `docs/ai-context/project-context.yaml` 和相关调用说明。任何 SQLite schema 改动须在 `src-tauri/src/db.rs::migrate()` 中添加兼容迁移,不能更改旧安装数据的含义或静默清空。
|
||||
|
||||
### 迁移与失败模型
|
||||
|
||||
1. 先增加脱敏 DTO 与回归测试;人的 API Key 展示/复制改为显式动作,常态列表不携带原值。此步骤不删 SQLite 原值。
|
||||
2. 以 Windows Credential Manager 为优先候选,在隔离测试中验证写入、读取、覆盖、删除、用户上下文、重启后读取与错误返回;若不满足要求,停止迁移并重新决策,不静默退回明文 SQLite。
|
||||
3. 迁移每条旧凭证时采取“写新存储 → 读取校验 → 切换引用 → 再清除旧列”的可恢复顺序;失败保留旧值并显示 `needs_repair`,不把失败伪装为 `not_configured`。设计并演练备份/恢复后再动真实用户数据;旧数据库备份可能仍含明文,不能宣称迁移后历史明文已消失。
|
||||
4. 凭证存储不可用时,代办操作 fail closed,项目自有 CI/运行链路照常;状态明确提示缺少/不可用/需修复,日志、错误文本和测试快照均不得回显原值。
|
||||
|
||||
### 验证路径
|
||||
|
||||
- 载体:Rust 纯函数及带内存数据库/模拟凭证存储的 `cargo test`;MCP 只读工具做真实 loopback/scoped 握手验证;前端展示由人终验。
|
||||
- 示例:`[cargo test] gitea_list_instances()` → JSON 不含 `access_token`;`get_project_resource_status(p1)` → 仅非秘密状态,p2 参数被 scope 拒绝。
|
||||
- 示例:`[cargo test] migrate_legacy_credential(模拟存储失败)` → 旧值仍可恢复、状态为 `needs_repair`、不泄露凭证。
|
||||
- 示例:`[MCP] create_pull_request(p1, head, base, title)` → 返回 PR 标识或可行动错误,不返回 token;不把生产部署作为测试副作用。
|
||||
|
||||
### 风险与决策闸门
|
||||
|
||||
- **同一 Windows 用户可读性**:本方案减少凭证进入 Agent 上下文与普通 DTO 的机会,不提供强进程隔离;验收不能写成“Agent 绝对无法获取”。
|
||||
- **旧用户数据迁移风险**:先用复制的匿名/模拟 DB 演练失败与回滚,再对真实数据执行;无验证、无备份方案不得迁移。
|
||||
- **功能回退风险**:API Key 显示/复制保留为人的显式操作;Gitea PR 创建和 webhook 签名必须回归。
|
||||
- **`Cargo.toml` 当前有既有未提交改动**:实施存储 adapter 若需改依赖,先确定改动归属并使用隔离工作区;不得混合提交或覆盖。
|
||||
- **范围膨胀风险**:Enterprise 非生产样板只验证一个真实操作;是否扩展第二种资源,由减少 Agent 阻塞次数、人工取密次数、失败定位耗时的实测结果决定。
|
||||
|
||||
## 任务拆解
|
||||
|
||||
### ✅ R1 — Gitea 与 webhook 输出脱敏
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/commands/gitea.rs, src/lib/commands.ts
|
||||
- depends: gitea-integration
|
||||
- acceptance: "agent_verify: [cargo test] gitea_list_instances / gitea_repo DTO 序列化 → 不含 access_token 或 webhook_secret;原有 Gitea 连接测试仍通过。human_verify: 设置页能显示实例并执行改 Token,不在常态 UI 或调试响应显示原值。"
|
||||
- human_verify: pending(需用户在隔离功能交付后终验真实 UI;agent 验证已通过)
|
||||
|
||||
### ✅ R2 — API Key 常态列表脱敏与人的显式查看
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/commands/api_keys.rs, src/lib/commands.ts, src/components/dashboard/ApiKeysPanel.tsx
|
||||
- depends: R1
|
||||
- acceptance: "agent_verify: [cargo test] get_api_keys() → 列表序列化不含 api_key,显式 reveal 按不存在 id 返回错误,MCP tools/list 无 reveal 工具。human_verify: 人主动点击后仍可查看/复制,默认列表不显示原值。"
|
||||
- human_verify: pending(需用户终验显式查看/复制;agent 验证已通过)
|
||||
|
||||
### ✅ R3 — Windows 本机凭证存储可行性与适配接口
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/credential_store.rs, src-tauri/src/lib.rs
|
||||
- depends: R2
|
||||
- acceptance: "[cargo test] 凭证存储适配器在模拟存储上完成 put/get/replace/delete 与失败注入;Windows 隔离测试重启后按引用读取同一测试值,失败时不回显值;不满足则停止后续迁移并记录证据。"
|
||||
|
||||
### ✅ R4 — Gitea 凭证兼容迁移与代办回归
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/db.rs, src-tauri/src/commands/gitea.rs, src-tauri/src/commands/git_ops.rs, src-tauri/src/credential_store.rs
|
||||
- depends: R1, R3
|
||||
- acceptance: "[cargo test] 旧 Gitea token 在模拟数据库中 write→verify→switch→clear;注入存储失败时旧值可恢复且状态 needs_repair;create_pull_request 与 webhook 注册仍使用有效凭证,响应不含 token。"
|
||||
|
||||
### ✅ R5 — webhook 签名凭证兼容迁移
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/db.rs, src-tauri/src/commands/gitea.rs, src-tauri/src/mcp_webhook.rs, src-tauri/src/credential_store.rs
|
||||
- depends: R1, R3, R4
|
||||
- acceptance: "[cargo test] 已登记 webhook_secret 的旧仓库经 write→verify→switch→clear 后仍可验证 webhook 签名;模拟存储失败时旧值可恢复,仓库 DTO 与日志均不含 secret。"
|
||||
|
||||
### ✅ R6 — API Key 凭证兼容迁移与显式查看回归
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/db.rs, src-tauri/src/commands/api_keys.rs, src-tauri/src/credential_store.rs, src/components/dashboard/ApiKeysPanel.tsx, src/lib/commands.ts
|
||||
- depends: R2, R3
|
||||
- acceptance: "[cargo test] 旧 API Key 迁移后列表仍无原值,人的显式 reveal 返回原值;模拟迁移失败保留可恢复数据且不产生半迁移的假成功。"
|
||||
|
||||
### ✅ R7 — 项目资源只读 readiness 与操作边界
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: src-tauri/src/commands/gitea.rs, src-tauri/src/mcp_tools.rs, src-tauri/src/mcp_server.rs, src-tauri/src/lib.rs, src/lib/commands.ts
|
||||
- depends: R4, R5
|
||||
- acceptance: "[cargo test] get_project_resource_status(p1) → configured/unavailable/needs_repair 有来源且不含凭证;p2 越界被拒绝;MCP tools/list 无 get_secret、任意 HTTP/SSH/SQL 代理。"
|
||||
|
||||
### ✅ R8 — 契约文档与 Enterprise 非生产样板终验
|
||||
- status: done
|
||||
- complexity: M
|
||||
- files: docs/ai-context/project-context.yaml, docs/ai-context/agent-button-system.md, docs/ai-context/project-brief.md, AGENTS.md, .blueprint/manifest.yaml, .blueprint/modules/project-resource-broker.md
|
||||
- depends: R6, R7
|
||||
- acceptance: "agent_verify: [MCP] Enterprise 项目调用只读 resource status → 返回非秘密 readiness;[cargo test] 模拟 Gitea 调用 create_pull_request → 仅返回 PR 标识/可行动错误,日志与调用结果无原始凭证;[CLI] git diff --check → exit 0。human_verify: 确认 Gitea/CI/服务器凭证仍在原用途位置,未创建真实 PR、触发生产部署或数据库写入。"
|
||||
- human_verify: pending(真实 UI 与凭证归属留待用户终验;agent 非生产路径已通过)
|
||||
|
||||
## 实施验证(合成数据,未接触真实生产密钥)
|
||||
|
||||
- R1–R2:DTO/API Key 常态列表序列化测试通过;人的显式 API Key reveal 只在本机 Tauri command,MCP 不暴露。
|
||||
- R3:Windows Credential Manager 随机测试引用跨进程读取并清理;模拟存储的写、读、覆盖、删除与失败注入通过。
|
||||
- R4–R6:旧值迁移、存储/数据库失败保留旧值、SQLite 迁移前备份及恢复演练、新建/更新/删除回归通过;模拟 Gitea PR 与 webhook 注册均只访问 `127.0.0.1`。
|
||||
- R7–R8:内存 Enterprise fixture 经真实 loopback MCP HTTP/SSE 完成 `initialize → tools/list → tools/call`,readiness 不含凭证,跨项目参数被拒绝。
|
||||
- 未验证:真实用户 DB/凭证迁移、真实 Gitea/CI/服务器、正式安装版 UI 人工终验和真实 PR/部署;现有备份若不含当前旧值会拒绝迁移并要求人工安排新备份,不会降级明文。
|
||||
|
||||
## 阶段退出标准
|
||||
|
||||
- P0(R1–R2):普通列表/DTO 不再携带原始值;不误称数据库已加密。
|
||||
- P1(R3–R6):本机代办凭证的受保护存储与兼容迁移可恢复,现有 Gitea/API Key/webhook 能力不回退。
|
||||
- P2(R7–R8):Agent 能获取 Enterprise 的非秘密 readiness,PR 代办通过模拟 Gitea 验证;真实 PR 试用需另行按交付流程授权。量化是否真减少人工传密和失败定位时间;未达到收益门槛,不扩成通用 Vault。
|
||||
@ -27,6 +27,7 @@ Git 多空间开发管理器,同时也是**项目 Agent 化的本地控制面*
|
||||
src/lib/commands.ts 所有 Tauri command 的前端封装(唯一调用入口)
|
||||
src-tauri/src/commands/ Rust 后端实现(每功能一个 .rs 文件)
|
||||
src-tauri/src/db.rs SQLite 建表 + migration 逻辑
|
||||
src-tauri/src/credential_store.rs Windows 本机受保护凭证存储与旧值兼容迁移(SQLite 仅保存引用)
|
||||
src-tauri/src/mcp_server.rs MCP Server 路由层 + SSE 协议(agent 调用炼境的入口)
|
||||
src-tauri/src/mcp_tools.rs MCP 工具 schema + dispatch + 实现(工具清单以 tools_list_result 为准)
|
||||
src-tauri/src/mcp_webhook.rs Gitea webhook 处理(push/PR 事件写入飞轮)
|
||||
|
||||
@ -139,6 +139,12 @@ acceptance 是 agent 自验的唯一判断依据,必须是可执行断言:
|
||||
|
||||
这套 scope 是单用户本机的误操作隔离,不是多租户认证。威胁模型变化前,不向项目配置写持久 token。
|
||||
|
||||
## 项目资源与凭证代办边界
|
||||
|
||||
`get_project_resource_status(project_id)` 是同一项目绑定上的只读 MCP/Tauri 投影,只报告 Gitea PR 与 webhook 的 `configured`、`not_configured`、`unavailable` 或 `needs_repair` 及非秘密来源。scoped MCP 连接拒绝跨项目参数;状态不含原始凭证、凭证引用、仓库 URL 或连接串。`configured` 仅表示本机代办凭证就绪,不代表远端 Gitea、CI 或部署已验证。
|
||||
|
||||
Gitea PR 继续使用既有 `create_pull_request` 有界操作;Windows 本机代办凭证由 Credential Manager 保护,SQLite 只存引用及非秘密元数据。旧值在显式操作时,先在数据库同目录以 SQLite `VACUUM INTO` 生成 `dev-manager.pre-credential-migration.db`,校验完整性和当前旧值确在备份中,再按写入、读取校验、事务切换引用并清除旧列的顺序迁移;备份不可用或失败则保留旧值并报告 `needs_repair`。此备份可能含历史明文,须按原数据库同等敏感级别保护,不能因当前列清空就宣称历史明文已消失。普通 Gitea/API Key DTO 不带原始值;人的 API Key 查看与复制仍通过本机 Tauri 的显式 reveal,MCP 不暴露该入口。Gitea Actions secrets、项目运行时密钥与服务器凭证仍留在各自原用途位置;炼境不提供 `get_secret` 或任意 SSH、SQL、HTTP、部署代理。同一 Windows 用户下的进程隔离不是本方案提供的安全保证。
|
||||
|
||||
## production 交付观测边界
|
||||
|
||||
项目可自主管理 `.lian-jing/capabilities.yaml` 的 `delivery.targets.production`,声明 Gitea Actions 的**精确 status context** 与公开 HTTP JSON 版本探针。炼境只读该声明;接入包不生成或覆盖项目的 production 配置。探针不接受 URL 凭证、认证头、查询参数、非 HTTP(S) 协议或本机/内网地址;连接时过滤域名解析出的私网地址,不走环境代理、不跟随重定向,并限制请求时间与响应大小。
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
src/lib/commands.ts 前后端契约(唯一前端入口)
|
||||
src-tauri/src/commands/ Rust 后端实现(一功能一文件)
|
||||
src-tauri/src/db.rs 数据库 schema + migration
|
||||
src-tauri/src/credential_store.rs Windows 本机凭证引用、校验与兼容迁移
|
||||
src-tauri/src/mcp_server.rs MCP Server(agent 调用炼境的入口)
|
||||
src-tauri/src/mcp_tools.rs MCP tool schema 与 dispatch
|
||||
src-tauri/src/commands/delivery_status.rs 项目交付信号聚合(Git / Gitea / 部署证据)
|
||||
|
||||
@ -15,7 +15,7 @@ contracts:
|
||||
- "src-tauri/src/commands/toolchain.rs"
|
||||
- "src-tauri/src/commands/cicd.rs"
|
||||
- "src-tauri/src/commands/gitea.rs"
|
||||
doc: "工具 schema、dispatch 与调用说明必须同步;production delivery profile 由项目维护,炼境只读 Gitea 部署 context 与公开版本探针,双证据相符才能报告 deployed。外部来源不可用时返回显式降级状态,不得伪造成功。verify_onboarding 必须同时返回静态接入 checks 与 configured/reachable/handshake/tools_ready/reload_required 运行态证据,配置存在不得替代运行可用。CI workflow 使用 plan_cicd_workflow 只读预览,用户确认相同 digest 后方可 apply_cicd_workflow 写入;该接口不接收凭证值且不触发 CI 或部署。"
|
||||
doc: "工具 schema、dispatch 与调用说明必须同步;get_project_resource_status 为项目 scoped 只读状态,仅返回 Gitea PR/webhook 的状态与来源,不返回凭证、引用、URL。MCP 不提供 reveal_api_key、get_secret 或任意 HTTP/SSH/SQL/部署代理。production delivery profile 由项目维护,炼境只读 Gitea 部署 context 与公开版本探针,双证据相符才能报告 deployed。外部来源不可用时返回显式降级状态,不得伪造成功。verify_onboarding 必须同时返回静态接入 checks 与 configured/reachable/handshake/tools_ready/reload_required 运行态证据,配置存在不得替代运行可用。CI workflow 使用 plan_cicd_workflow 只读预览,用户确认相同 digest 后方可 apply_cicd_workflow 写入;该接口不接收凭证值且不触发 CI 或部署。"
|
||||
|
||||
- label: 被管理项目身份上下文
|
||||
paths:
|
||||
@ -30,7 +30,7 @@ contracts:
|
||||
paths:
|
||||
- "src-tauri/src/commands/**/*.rs"
|
||||
- "src/lib/commands.ts"
|
||||
doc: "新增 command 时前后端必须同步。Agent readiness 必须保留 configured/reachable/handshake/tools_ready/reload_required 分层;Windows autostart 默认关闭,只允许正式安装版,debug/target 构建必须拒绝启用。"
|
||||
doc: "新增 command 时前后端必须同步。Gitea/API Key 普通 DTO 不含原始凭证;API Key 仅本机 UI 显式 reveal,get_project_resource_status 仅返回非秘密状态;旧 SQLite 明文列按 write→verify→事务切换引用并清除的顺序兼容迁移,失败需可恢复,不能降级明文。Agent readiness 必须保留 configured/reachable/handshake/tools_ready/reload_required 分层;Windows autostart 默认关闭,只允许正式安装版,debug/target 构建必须拒绝启用。"
|
||||
|
||||
architecture_docs:
|
||||
- "AGENTS.md"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user