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

7.5 KiB
Raw Blame History

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

目标:

  • 定义 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:

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 内部存储接口:

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 生成入口:

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:

mindspace://users/<userId>/conversations/<sessionId>

推荐 manifest:

{
  "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:

input_image
input_file
generated_image
generated_file
page
public_html
long_image
thumbnail
docx
pdf

6. Metadata Tables

首选新增:

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

短期兼容:

{
  "sandboxRoot": "/absolute/local/MindSpace/<userId>",
  "serverPath": "/absolute/local/mindspace-sandbox-mcp.mjs"
}

长期目标:

{
  "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 关联 pageIdpublicationId
  • 同一 session 多次生成按 sortOrdercreatedAt 稳定排序。

P3:

  • MindSpace Service 停止时,Memind App 明确返回服务不可用。
  • 替换 local storage root 不需要改 Memind App / Goose 业务逻辑。

9. Non-Goals

当前阶段不做:

  • 不迁移生产 103。
  • 不切换 NAS/S3。
  • 不删除旧 URL helper。
  • 不改变现有 /MindSpace/* 公开访问行为。
  • 不改变 Goose 当前可用的 sandboxRoot 兼容路径。