mindspace: close authority boundaries

This commit is contained in:
john
2026-07-27 15:34:35 +08:00
parent dfab78c75a
commit e94052ff24
78 changed files with 11962 additions and 2162 deletions
@@ -16,7 +16,8 @@
| 位置 | 行为 |
|------|------|
| `mindspace-public-finish-sync.mjs` | `edit_file` 读取 baseline + `old_str`/`new_str` patch,再 `writeFileSync` 覆盖 |
| `server.mjs` | Finish / SSE 流式事件调用 `materializePublicHtmlWritesFromSessionEvent` |
| `mindspace-public-finish-service.mjs` | 在 MindSpace 内调用流式物化与最终 Finish sync,并生成 canonical URL |
| `server/portal-session-routes.mjs` | Finish / SSE 流式事件只调用 `publicFinishService`,等待流式写入完成后再标记交付 ready |
### 守卫
@@ -25,6 +26,136 @@
---
## 1.1 Chat SaveHTML 修复与落盘权威必须在 MindSpace
### 必须保留
| 位置 | 行为 |
|------|------|
| `mindspace-chat-save-service.mjs` | 修复错误私有图片引用、物化私有图片并受限写回 `public/*.html`;预览缩略图生成与 sidecar 写入 |
| `mindspace-chat-save-service.mjs` | quick share 的私有资源内联、`public/shared/*.html` 写入和 public URL 生成 |
| `mindspace-server-adapter-contract.mjs` | `chatSaveService.materializeWorkspaceHtml``ensurePreviewThumbnail``renderPreviewThumbnailSvg``createSharedHtml``readWorkspaceHtml` 同时暴露给 local / remote adapter |
| `server.mjs` | 只调用 `mindSpaceChatSave.materializeWorkspaceHtml`,禁止重新读取 `storageRoot` 或直接 `writeFile` |
| `server/portal-mindspace-chat-save-routes.mjs` | 预览缩略图与分析/保存流程只调用 `getMindSpaceChatSave()`,禁止直接解析 `publishDir` 或调用 `ensureWorkspaceHtmlThumbnail` |
| `server/portal-mindspace-chat-share-routes.mjs` | quick share 只调用 `createSharedHtml`quick Plaza 的 workspace HTML 读取只调用 `readWorkspaceHtml`;禁止直接解析 publishDir、写文件、读取 storage root 或生成 public URL |
这条边界用于保证 `split-service` 下 Portal 不再成为 Chat Save
的物理文件写权威。修复与图片物化继续保持幂等和 best-effort
真正的工作区写失败仍须明确返回错误,禁止静默回退到 Portal 本地写入。
### 守卫
- 单测:`mindspace-chat-save-service.test.mjs`
- local / remote / RPC`mindspace-local-runtime-services.test.mjs`
`mindspace-remote-server-adapter.test.mjs`
`mindspace-service/mindspace-rpc-server.test.mjs`
- 源码门禁:`scripts/verify-mindspace-authority-boundary.mjs`
- 综合验证:`npm run verify:mindspace-publish-guards`
- runtime 双边一致性:构建 Portal 与 MindSpace service runtime 后执行
`npm run verify:mindspace-publish-guards:full`
---
## 1.2 Conversation Artifact:登记与 manifest 写入权威必须在 MindSpace
### 必须保留
| 位置 | 行为 |
|------|------|
| `mindspace-conversation-package-artifact-service.mjs` | 从逻辑 user/session/artifact ref 解析工作区文件,登记 public HTML、聊天 DOCX、发布长图和通用 workspace 写入,并刷新 package manifest |
| `mindspace-server-adapter-contract.mjs` | `conversationArtifactService` 的四类登记方法同时暴露给 local / remote adapter`conversationPackageRegistry` 的远程契约只保留读取方法 |
| `server/portal-session-routes.mjs` | Finish 只传 user/session/relativePath/messageId,禁止把 `publishDir` 传入 artifact 登记 |
| `server/portal-mindspace-chat-save-routes.mjs` | DOCX 生成后调用 `registerChatDocxArtifact`,禁止直接取得 conversation package registry |
| `server.mjs` | public HTML 与发布长图登记只调用 `mindSpaceConversationArtifacts`;禁止直接 `putObjectForSession``recordArtifact``writeManifestForSession` |
该边界保证 local 与 split-service 使用同一个产物登记入口。MindSpace
负责 backing file 校验、确定性 artifact ID、对象写入和 manifest
刷新;Portal 仍只负责鉴权、Finish 时序与生成结果转发。
Finish 中受第 1 节保护的 `edit_file` 落盘兼容逻辑已迁入
`publicFinishService`,但仍复用同一纯实现和回归用例;禁止在 Portal
增加本地写回 fallback,也不得破坏已验证的覆盖语义。
### 守卫
- 单测:`mindspace-conversation-package-artifact-service.test.mjs`
- local / remote / RPC`mindspace-local-runtime-services.test.mjs`
`mindspace-remote-server-adapter.test.mjs`
`mindspace-service/mindspace-rpc-server.test.mjs`
- Portal 路由:`server/portal-session-routes.test.mjs`
`server/portal-mindspace-chat-save-routes.test.mjs`
- 源码门禁:`scripts/verify-mindspace-authority-boundary.mjs`
- 综合验证:`npm run verify:mindspace-publish-guards`
---
## 1.3 Workspace 读交付:物理路径与 canonical URL 必须在 MindSpace
### 必须保留
| 位置 | 行为 |
|------|------|
| `mindspace-workspace-publication-delivery-service.mjs` | 解析 `/MindSpace` 请求、校验 backing file 与 delivery contract、读取 HTML/二进制、补私有资源、生成缩略图/长图、扫描最近 HTML,并验证 Agent Run deliverables |
| `mindspace-publications.mjs` | `resolvePublic` 返回 `workspacePublicUrl`canonical workspace URL 不由 Portal 拼接 |
| `mindspace-public-finish-service.mjs` | Finish 内完成 HTML 交付完整性检查、H5/微信 Page Data preparation、微信 HTML 链接交付与 fresh thumbnail 校验/修复;跨 RPC 返回的结果必须移除绝对路径与 HTML 原文 |
| `mindspace-local-runtime-services.mjs` | 构造 `workspacePublicationDeliveryService``workspacePageDeliveryService`local/remote 使用同一 adapter contract |
| `server/portal-workspace-publication-delivery.mjs` | 只把逻辑 request path 交给 MindSpace,并代理 HTML/Base64 body;禁止读取、校验或返回本地绝对路径 |
| `server/portal-publication-routes.mjs` | `/u/:slug/public` 调用 `readOwnerPublicAsset`publication redirect 使用 `workspacePublicUrl` |
| `server/portal-session-routes.mjs` | 先同步页面记录,再调用 `preparePageDataAfterFinish` 并消费逻辑结果;禁止解析 `publishDir``storageRoot``h5Root` |
| `server/portal-integration-services-bootstrap.mjs` + `wechat-mp.mjs` | 微信只接收 `prepareWechatPageDataDelivery``prepareWechatHtmlDelivery``ensureWechatFreshPageThumbnails` 逻辑能力;禁止注入或读取 MindSpace 的 pool/storage path / publishDir / backing file path |
| `server/portal-mindspace-asset-routes.mjs` | 下载和 from-asset 只消费 `readAssetContent` / `readPublicAssetContent` 的 Base64 body |
Portal 可以继续负责鉴权、viewer-specific HTML 注入、CSP/缓存响应头和
repair turn 的触发,但不能获得 backing file 路径,也不能在 remote
模式下回退读取 Portal 本机目录。
### 守卫
- 核心服务:`mindspace-workspace-publication-delivery-service.test.mjs`
- Portal edge`server/portal-workspace-publication-delivery.test.mjs`
`server/portal-publication-routes.test.mjs`
- local / remote / RPC`mindspace-local-runtime-services.test.mjs`
`mindspace-remote-server-adapter.test.mjs`
`mindspace-service/mindspace-rpc-server.test.mjs`
- 源码门禁:`scripts/verify-mindspace-authority-boundary.mjs`
- 综合验证:`npm run verify:mindspace-publish-guards`
- runtime 双边一致性:构建两侧 runtime 后执行
`npm run verify:mindspace-publish-guards:full`
---
## 1.4 Agent MCP:逻辑 workspace/package 与 scoped token 必须成套
### 必须保留
| 位置 | 行为 |
|------|------|
| `mindspace-workspace-tool-service.mjs` | 只接受逻辑 `workspaceRef`、当前 `sessionId/packageId` 和相对路径;阻止绝对路径、路径穿越、symlink escape 与浏览器持久存储 |
| `mindspace-mcp-scoped-token.mjs` | token 同时绑定 user、session、package、workspace 与工具 allowlist,并校验签名和有效期 |
| `mindspace-service/mindspace-rpc-server.mjs` | `/mindspace/v1/mcp/:tool` 从 token 注入作用域,拒绝请求体覆盖 user/session/package/workspace |
| `mindspace-sandbox-mcp.mjs` | scoped 模式下通用 workspace 工具和 `generate_long_image` 只经 MindSpace ServiceDOCX 仅在临时目录生成,再通过内部 `write_binary_file` 上传 |
| `capabilities.mjs` + `user-auth.mjs` | 取得真实 session 后才生成 scoped token;签名 secret 不得进入 Goose extension env |
| `mindspace-conversation-package-artifact-service.mjs` | 非公开 workspace 写入也必须直接登记 artifact 并刷新 manifest,不能把 asset sync 的结果当作唯一登记保证 |
| `mindspace-workspace-tool-service.mjs` | 二进制写入和长图目标写入必须落盘、登记 artifact、刷新 manifest,并且响应不得暴露绝对路径 |
`publish_page` 在 scoped endpoint/token/workspace/session/package 任一项
缺失时不得暴露。保留旧本地文件工具只是未配置 scoped split-service
时的兼容路径,不得让它成为 remote 模式的静默 fallback。
scoped 模式下 `generate_docx` / `generate_long_image` 也不得在 RPC
失败时回退写 Goose 本地目标路径。
### 守卫
- 单测:`mindspace-workspace-tool-service.test.mjs`
`mindspace-mcp-scoped-token.test.mjs`
`mindspace-sandbox-mcp.test.mjs``capabilities.test.mjs`
- RPC / policy`mindspace-service/mindspace-rpc-server.test.mjs`
`user-auth.test.mjs``tkmind-proxy.test.mjs`
- 源码门禁:`scripts/verify-mindspace-authority-boundary.mjs`
- 综合验证:`npm run verify:mindspace-publish-guards`
---
## 2. 聊天 Finish:禁止清空对话 / 禁止暴露内部前缀
### 症状
@@ -123,6 +254,7 @@ npm run verify:mindspace-publish-guards:full
- [ ] 是否仍 merge 本地与服务器消息(Finish / UpdateConversation)?
- [ ] 是否仍过滤/剥离用户消息中的 agent-only 前缀?
- [ ] `edit_file` 是否仍会 materialize 到 `MindSpace/<userId>/public/`
- [ ] Chat Save HTML 修复与落盘是否仍只经 `chatSaveService`
- [ ] 是否已跑 `npm run verify:mindspace-publish-guards`
- [ ] 是否更新了本页或相关单测?