# 云端原生市场 V1 契约 目标:单运营者自托管可信 DSH bundle 市场。所有核心操作均有 Web 入口;不包含计费、公共社区、多租户或不可信插件沙箱。旧 Python 市场单独启动,新部署默认不加载它。 ## 运行与认证 入口为 `app.cloud_main:app`,独立于旧 `app.main`。`MESSAGE_DATA_DIR` 为仓库外可写根,保存 catalog 与 profiles;同一实例只运行一个 API worker,负责子进程。每个 profile 独立 Node 进程、安装状态和 MCP 会话。 管理API默认使用Supabase管理员账号与HttpOnly/Secure/SameSite cookie、精确Origin和CSRF。无项目配置时可启动锁定引导,不能注册首位管理员。MCP使用页面创建的profile scoped API key,每次请求查Supabase哈希/撤销/过期。详细Auth接口与SQL约束见 `supabase-auth.md`;不需要service_role。插件秘密仍只允许精确{$env:变量名}引用,值仅注入目标worker。 `MESSAGE_ALLOWED_HOSTS` 与 `MESSAGE_ALLOWED_ORIGINS` 明确配置,拒绝不匹配 Host/Origin;无 Origin 的原生客户端可用。反向代理终止 TLS,只有经过此鉴权网关才可远程访问;内部 DSH HTTP 仅 loopback。 ## API(统一 JSON;错误为 detail 文本) - GET `/api/native/health`:免认证仅 `{status:'healthy'}` - GET `/api/native/catalog`:`{items:[{id,name,version,description,sha256,bytes}]}`;id 为 SHA256 - POST `/api/native/catalog`:multipart `file` 上传独立 tgz;检查归档路径、元数据、bundle 声明、依赖来源和大小;重复相同内容幂等,同名同版本不同内容拒绝;返回 artifact - GET `/api/native/catalog/{id}/download`:鉴权下载归档 - GET `/api/native/profiles`:`{items:[{id,status,generation,restart_required,last_error}]}` - POST `/api/native/profiles`:`{id}` 创建空 profile,返回状态;id 仅小写字母/数字/短横线 - GET `/api/native/profiles/{id}`:`{id,status,generation,restart_required,last_error,bundles,plugins,configured_tools,active_tools}` - POST `/api/native/profiles/{id}/install`:`{artifact_id}`;同名新版本是升级,返回 `{operation,profile}`,标记待重启 - POST `/api/native/profiles/{id}/enable`、`/disable`:`{name}`,返回 `{operation,profile}`,标记待重启 - POST `/api/native/profiles/{id}/restart`、`/stop`:无body,返回状态;restart关闭旧worker和会话,启动新generation,失败可诊断/恢复 - POST `/api/native/profiles/{id}/call`:`{name,arguments:{}}`;只调用正在运行worker的真实MCP,不用临时CLI假装活跃工具 - `/mcp/{id}`:鉴权Streamable HTTP代理,正确转发POST/GET/DELETE、MCP会话/协议头与SSE;隔离profile token和后端会话 Web登录会话使用HttpOnly cookie,刷新页面可恢复,不在localStorage保存token。默认页为原生市场,可选择profile、查看目录与安装版本、上传/下载、安装/升级/启停、看到待重启状态、重启/停止、调用工具、查看可复制的MCP配置。旧页面标记legacy并保持兼容路由。 ## 交付验收 真实浏览器与真实SDK覆盖:未登录拒绝、上传/目录、profile创建、安装、待重启、重启后工具可用、调用、禁用与重启后拒绝、升级新值、两profile状态与令牌隔离、同profile多MCP会话、错误恢复、API进程关闭回收workers、冷启动持久状态恢复。消费者源码/lock哈希不因安装/升级变化。 部署包提供轻量Python依赖锁、Node lock、前端构建、非root容器、健康检查、数据卷和反向代理TLS示例;不执行实际发布。所有用户文档为Markdown。 ## 操作与恢复补充 安装/升级/启停/配置/卸载/重启/停止为异步操作,接受 `Idempotency-Key`,返回 HTTP 202 `{operation_id, status}`。`GET /api/native/operations?profile_id=` 返回 `{items:[{id,profile_id,kind,status,result,error,created_at,updated_at}]}`,`GET /api/native/operations/{id}` 读取单项;status 为 queued/running/succeeded/failed/cancelled。同 key 重复请求返回同一操作,不重复执行;不同payload同key拒绝。`POST /api/native/operations/{id}/cancel` 申请取消,终态可持续查询;已不可取消则409。页面刷新后可重新读取任务。启动时未完成操作标为 interrupted/failed,不能报告成功。 新增 `POST /api/native/profiles/{id}/remove`:`{name}`。卸载先停止使用该包的worker,再执行离线包移除,不假装全量事务;失败保留诊断与明确的实际状态。操作返回中区分期望安装/启用状态、正在运行的generation与待重启状态。 配置接口:POST `/api/native/profiles/{id}/configure`,body `{entry_id, config:{}}`,同一异步操作契约。plugins条目提供entryId/entry_id、patchId/row_id、raw config和readOnlyReason;管理读取不挂载第三方apply。安装/升级前停止worker,完成后待人工重启;普通配置和启停可保留旧worker直到重启。 存活GET `/api/native/health` 与就绪GET `/api/native/readiness` 分开;后者在应运行profile未就绪时503。每次请求在读取body之前鉴权。 ## Auth接口 GET `/api/native/auth/status` 返回配置/登录/CSRF/存储状态;POST `/auth/login` 输入email/password;POST `/auth/logout`撤销当前Supabase会话;POST `/auth/recover`发默认PKCE邮件;GET `/auth/recovery/callback`消费一次code;POST `/auth/password`仅允许独立恢复cookie。以上路径省略共同 `/api/native` 前缀。 GET/POST `/api/native/profiles/{id}/keys` 列表/创建(name、expires_in_days),DELETE `/{key_id}`撤销,GET `/api/native/auth/audit`查固定事件。密钥只创建响应有明文,其他响应无哈希或secret。session绑定key ID,撤销中断本进程现有流。