Files
memind/docs/mindspace-boundary-audit-20260702.md
2026-07-02 21:03:14 +08:00

162 lines
7.3 KiB
Markdown

# MindSpace 解耦 P0 边界审计
日期: 2026-07-02
分支: `codex/mindspace-decouple-20260702`
目标:
- 找出当前代码中直接依赖 MindSpace 物理路径、公开 URL、agent sandbox policy、MindSpace DB 表的边界点。
- 为后续 P1 `MindSpaceService`、P2 conversation package、P3 独立进程拆分提供改造顺序。
- 本文只做审计和分层,不改变运行行为。
## 1. 审计结论
当前 MindSpace 已经具备资产、页面、发布、agent job 的基础模型,但边界仍混在 Portal 单体里:
- 物理 workspace 路径由 `user-publish.mjs``user-space.mjs``server.mjs``capabilities.mjs` 共同参与。
- 公开 URL 由 `user-publish.mjs``mindspace-assets.mjs``mindspace-publications.mjs``server.mjs` 等处生成或修正。
- Agent sandbox policy 仍以 `sandboxRoot` / `SANDBOX_ROOT` 为核心契约。
- DB 层已有 `h5_page_records.source_session_id``h5_page_records.source_message_id``h5_agent_jobs.session_id`,但 `h5_assets` 还缺 conversation provenance。
- 现有回归守卫重点保护 `public/*.html` materialize、Finish merge、用户消息内部前缀隐藏,这些路径后续改造时不能绕过。
架构判断:
- P1 先抽 `MindSpaceService` 和 storage adapter 边界,不拆进程。
- P2 先落 conversation package 元数据和 manifest,不立即替换所有文件路径。
- P3 以后再把 MindSpace Service 独立进程化。
- 103/105 只作为生产部署说明,不作为开发或架构依赖。
## 2. 物理路径边界
核心文件:
| 文件 | 当前职责 | 风险 | P1 处理方式 |
| --- | --- | --- | --- |
| `user-publish.mjs` | `PUBLISH_ROOT_DIR = MindSpace`、用户发布目录、公开 URL helper、skill 约束文本 | URL 和路径耦合在同一层 | 保留兼容 helper,但新代码走 `MindSpaceService` URL/storage 接口 |
| `user-space.mjs` | `MINDSPACE_STORAGE_ROOT`、workspace 分区、上传镜像、agent hints | workspace 和 asset storage 同时出现 | 把 storage root、workspace root 分离为 adapter 能力 |
| `server.mjs` | watcher、static route、shared upload、public serving | Portal 直接掌握所有路径 | P1 先集中成 MindSpace route/service facade |
| `mindspace-sandbox-mcp.mjs` | OS 层 `SANDBOX_ROOT` 文件操作 | Goose 以 raw fs path 作为契约 | P4 改成 `workspaceRef` / `packageId`,短期保留兼容 |
| `capabilities.mjs` | 注入 sandbox MCP env 和 args | policy 直接依赖 `sandboxRoot` | 新增 policy adapter,避免业务层自己拼 path |
主要路径形态:
```text
MindSpace/<userId>/public/*.html
MindSpace/<userId>/oa/*
data/mindspace/users/<userId>/*
SANDBOX_ROOT=<absolute workspace path>
MINDSPACE_STORAGE_ROOT=<absolute storage path>
```
## 3. URL 权威边界
核心文件:
| 文件 | 当前职责 | 风险 | P1 处理方式 |
| --- | --- | --- | --- |
| `user-publish.mjs` | `buildPublicUrl()``buildPublicZonePageUrl()` | 调用方可能把 helper 当长期权威 | 标记为 legacy compatibility,新增 canonical URL service |
| `mindspace-assets.mjs` | public temp image URL | asset public URL 与 storage key 绑定 | 改成 `MindSpaceService.createPublicAssetUrl()` |
| `mindspace-publications.mjs` | publication response、public URL、标准图片替换 | 发布 URL 权威分散 | publication canonical URL 统一收口 |
| `server.mjs` | `/MindSpace/*` route、redirect、shared upload public URL | Express route 同时做 URL 修复和文件服务 | route 仅调用 MindSpace service facade |
| `mindspace-chat-save.mjs` | 修正聊天中的 MindSpace 链接 | 后续仍需兼容旧 URL | 保留兼容,新增 canonicalize API |
短期规则:
- 不删除旧 URL helper。
- 新增代码不得直接拼 `/MindSpace/<userId>/...`
- 所有新 public URL 生成必须经过 MindSpace service facade。
## 4. Agent Policy 边界
核心文件:
| 文件 | 当前职责 | 风险 | P1/P4 处理方式 |
| --- | --- | --- | --- |
| `capabilities.mjs` | 将 sandbox MCP 注入 Goose extension | policy 里暴露 raw path | 先增加 policy builder 边界,后续改 workspaceRef |
| `mindspace-agent-runner.mjs` | agent job claim 后创建 goosed 运行上下文 | job 与 workspace/path/publish layout 混合 | job runner 改为请求 MindSpace workspace capability |
| `user-space.mjs` | hints 中告诉 agent 目录和公开链接格式 | agent 直接学习物理路径 | hints 改为逻辑 workspace + 兼容说明 |
| `mindspace-sandbox-mcp.mjs` | read/write/edit/create_dir | 只能处理 filesystem backend | 后续新增 MindSpace MCP bridge |
长期 policy 目标:
```json
{
"mindspace": {
"workspaceRef": "mindspace://users/<userId>/conversations/<sessionId>",
"packageId": "cp_xxx",
"apiBaseUrl": "https://mindspace.example.com",
"accessToken": "scoped-short-lived-token"
}
}
```
## 5. DB 和 Provenance 边界
已有基础:
- `h5_assets`: asset 基础元数据,当前没有 `source_session_id``source_message_id``source_run_id`
- `h5_asset_versions`: `storage_key` 是当前 asset 到物理存储的主要桥。
- `h5_page_records`: 已有 `source_session_id``source_message_id``source_asset_id`
- `h5_page_versions`: 已有 `source_snapshot_json`,可承载页面生成时的上下文。
- `h5_publish_records`: 保存 `public_url`,是公开 URL 现有权威记录。
- `h5_publication_asset_refs`: publication 与 asset 引用关系。
- `h5_agent_jobs`: 已有 `session_id``result_page_id``result_asset_id`
- `h5_agent_runs`: 已有 `agent_session_id``request_id`
缺口:
- 没有 conversation package 表。
- 没有 package 与 asset/page/publication/job/message 的统一 artifact 关系表。
- Asset 层没有完整对话来源,导致“这次对话生成了哪些文件”只能间接推断。
建议新增:
- `h5_conversation_packages`
- `h5_conversation_artifacts`
过渡策略:
- 先通过 `h5_page_records.source_session_id``h5_agent_jobs.session_id` 回填页面/任务。
- 新增 asset 时尽量携带 `session_id``message_id``run_id` 到 artifact 表。
- 不急着修改旧 asset schema,优先用 junction table 降低迁移风险。
## 6. 回归守卫
后续改这些区域前必须跑:
```bash
npm run verify:mindspace-publish-guards
npm run verify:mindspace-publish-guards:full
```
重点保护:
- `mindspace-public-finish-sync.mjs`: `edit_file` 必须 materialize 到 `public/*.html`
- `chat-finish-sync.mjs`: Finish / UpdateConversation 必须 merge,禁止盲覆盖。
- `conversation-display.mjs``src/utils/message.ts`: 用户消息不得展示 routing/skill 内部前缀。
- `src/hooks/useTKMindChat.ts`: `syncSessionMessages` merge + retry。
- `server.mjs`: session snapshot 缓存必须保守。
## 7. 第一阶段落点
P1 最小代码目标:
- 新增 `MindSpaceStorageAdapter` 契约和 local fs adapter 包装层。
- 新增 canonical public URL facade。
- 新增 conversation package manifest 纯函数。
- 先不替换现有 route,不改生产行为。
P2 最小产品目标:
- 为每个 chat session 懒创建 package。
- 新上传文件、生成文件、页面、发布记录写入 artifact 归属。
- H5 增加 package view。
- 历史数据 best-effort 回填。
本轮已经开始的安全基线:
- 当前工作必须在 `codex/mindspace-decouple-20260702`
- `main` 本地 pre-commit 已阻止直接提交。
- 不允许把 103 作为本地开发依赖。