Files
memind/docs/mindspace-service-contract.md

270 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MindSpace Service Contract
日期: 2026-07-02
状态: Draft for P1/P2
生产拓扑更新(2026-07-03:
- 103 的 MindSpace Service 已独立部署在 `/Users/john/MindSpace`,服务为 `cn.tkmind.mindspace-service`,端口 `8082`
- Portal live 目录仍是 `/Users/john/Project/Memind`,但它不再是 MindSpace Service 的根目录。
- `/Users/john/Project/Memind/MindSpace` 只能当旧链路兼容/存量目录处理;新排障和新配置必须优先看 [103 runtime topology](./103-runtime-topology.md)。
目标:
- 定义 MindSpace 从 Memind Portal 单体拆出前必须稳定下来的 API、存储、URL、权限和 package 契约。
- 允许先在单体内实现,再独立成进程。
- 保证 Memind App 和 Goose Runtime 不依赖 MindSpace 的物理部署位置。
## 1. 边界原则
MindSpace Service 是以下对象的唯一权威:
- Space
- Category
- Asset
- Asset Version
- Page
- Page Version
- Publication
- Conversation Package
- Package Manifest
- Canonical Public URL
- Storage Key 到 backing object 的校验
Memind App 只调用 API,不直接读写 `MindSpace/``data/mindspace/`
Goose Runtime 只通过 MindSpace API/MCP 获得 scoped workspace capability,不直接拥有 MindSpace 业务状态。
## 2. API Surface
建议从当前 `/api/mindspace/v1/*` 延续,先在单体内实现兼容 facade:
```text
GET /api/mindspace/v1/spaces/current
GET /api/mindspace/v1/assets
POST /api/mindspace/v1/assets/uploads
GET /api/mindspace/v1/assets/:assetId/download
GET /api/mindspace/v1/pages
POST /api/mindspace/v1/pages
GET /api/mindspace/v1/pages/:pageId
POST /api/mindspace/v1/pages/:pageId/publish
GET /api/mindspace/v1/publications/:publicationId
GET /api/mindspace/v1/conversation-packages
POST /api/mindspace/v1/conversation-packages/ensure
GET /api/mindspace/v1/conversation-packages/:packageId
GET /api/mindspace/v1/conversation-packages/:packageId/manifest
POST /api/mindspace/v1/conversation-packages/:packageId/artifacts
```
所有响应必须包含稳定 ID,不把物理路径作为前端契约。
## 3. Storage Adapter
MindSpace Service 内部存储接口:
```ts
interface MindSpaceStorageAdapter {
putObject(key, body, options)
getObject(key)
statObject(key)
deleteObject(key)
listObjects(prefix, options)
copyObject(sourceKey, targetKey, options)
createReadStream(key)
createWriteStream(key, options)
getSignedUrl(key, options)
}
```
Adapter 要求:
- `key` 是相对 storage root 的逻辑 object key,禁止绝对路径。
- local fs adapter 可以把 key 映射到磁盘。
- NAS adapter 可以把 key 映射到共享挂载。
- S3 adapter 可以把 key 映射到 object prefix。
- 业务层不得依赖 adapter 的物理实现。
## 4. Canonical URL
URL 生成入口:
```ts
createPublicPageUrl({ userId, publicationId, pageId, slug })
createPublicAssetUrl({ userId, assetId, variant })
canonicalizeMindSpaceUrl(inputUrl)
validatePublicBackingObject({ publicationId })
```
规则:
- 新代码不得直接拼 `/MindSpace/<userId>/...`
- 当前分支新增的 `mindspace-canonical-url.mjs` 是 P1 facade 起点;后续旧 helper 迁移到它后,再接入 backing object 校验。
- 当前分支新增的 `mindspace-service.mjs` 是组合门面起点;它先组合 package manifest、storage adapter、canonical URL,不接入现有运行链路。
- 旧 URL helper 暂时保留为 compatibility layer。
- public URL 必须能反查到 publication 或 asset。
- URL 返回前应校验 backing object 存在,或明确返回 pending 状态。
## 5. Conversation Package
Conversation package 是“每次对话文件夹包”的业务抽象。
逻辑 URI:
```text
mindspace://users/<userId>/conversations/<sessionId>
```
推荐 manifest:
```json
{
"schemaVersion": 1,
"packageId": "cp_xxx",
"userId": "u_xxx",
"sessionId": "s_xxx",
"title": "对话标题",
"storagePrefix": "users/u_xxx/conversations/s_xxx",
"artifacts": [
{
"artifactId": "ca_xxx",
"kind": "public_html",
"assetId": "asset_xxx",
"pageId": "page_xxx",
"publicationId": "pub_xxx",
"messageId": "msg_xxx",
"agentRunId": "run_xxx",
"displayName": "report.html",
"mimeType": "text/html",
"sizeBytes": 12345,
"storageKey": "users/u_xxx/conversations/s_xxx/public/report.html",
"canonicalUrl": "https://...",
"createdAt": 1783000000000
}
]
}
```
Artifact kind:
```text
input_image
input_file
generated_image
generated_file
page
public_html
long_image
thumbnail
docx
pdf
```
## 6. Metadata Tables
首选新增:
```sql
CREATE TABLE h5_conversation_packages (
id VARCHAR(64) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
session_id VARCHAR(128) NOT NULL,
title VARCHAR(255) DEFAULT NULL,
status ENUM('active', 'archived', 'deleted') NOT NULL DEFAULT 'active',
storage_prefix VARCHAR(512) DEFAULT NULL,
manifest_asset_id CHAR(36) DEFAULT NULL,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL,
UNIQUE KEY uniq_conversation_package_session (user_id, session_id)
);
CREATE TABLE h5_conversation_artifacts (
id VARCHAR(64) PRIMARY KEY,
package_id VARCHAR(64) NOT NULL,
asset_id CHAR(36) DEFAULT NULL,
page_id CHAR(36) DEFAULT NULL,
publication_id CHAR(36) DEFAULT NULL,
agent_run_id CHAR(36) DEFAULT NULL,
message_id VARCHAR(128) DEFAULT NULL,
role VARCHAR(32) DEFAULT NULL,
artifact_kind VARCHAR(64) NOT NULL,
display_name VARCHAR(255) DEFAULT NULL,
storage_key VARCHAR(512) DEFAULT NULL,
canonical_url VARCHAR(512) DEFAULT NULL,
sort_order INT NOT NULL DEFAULT 0,
created_at BIGINT NOT NULL,
KEY idx_conversation_artifacts_package (package_id),
KEY idx_conversation_artifacts_asset (asset_id),
KEY idx_conversation_artifacts_page (page_id),
KEY idx_conversation_artifacts_message (message_id)
);
```
迁移原则:
- 先新增表,不改旧表语义。
- 新写入链路双写 artifact 归属。
- 历史数据 best-effort 回填。
- 回填失败不影响旧页面访问。
## 7. Goose Capability
短期兼容:
```json
{
"sandboxRoot": "/absolute/local/MindSpace/<userId>",
"serverPath": "/absolute/local/mindspace-sandbox-mcp.mjs"
}
```
长期目标:
```json
{
"workspaceRef": "mindspace://users/<userId>/conversations/<sessionId>",
"packageId": "cp_xxx",
"apiBaseUrl": "https://mindspace.example.com",
"accessToken": "short-lived-scoped-token",
"allowedTools": ["read_file", "write_file", "edit_file", "publish_page"]
}
```
权限要求:
- Token 必须绑定 user、session、package。
- Token 必须有过期时间。
- Tool allowlist 必须由 Memind/MindSpace policy 共同决定。
- Goose 不得凭 token 访问其它用户 package。
## 8. Acceptance Tests
P1:
- local fs adapter 拒绝绝对 key 和 `..` traversal。
- canonical URL 只通过 facade 生成。
-`MindSpace/<userId>/public/*.html` 链路不变。
- `npm run verify:mindspace-publish-guards` 通过。
P2:
- 上传图片后 package manifest 出现 `input_image`
- 生成 HTML 后 package manifest 出现 `public_html`
- 发布页面后 artifact 关联 `pageId``publicationId`
- 同一 session 多次生成按 `sortOrder``createdAt` 稳定排序。
P3:
- MindSpace Service 停止时,Memind App 明确返回服务不可用。
- 替换 local storage root 不需要改 Memind App / Goose 业务逻辑。
## 9. Non-Goals
当前阶段不做:
- 不迁移生产 103。
- 不切换 NAS/S3。
- 不删除旧 URL helper。
- 不改变现有 `/MindSpace/*` 公开访问行为。
- 不改变 Goose 当前可用的 `sandboxRoot` 兼容路径。