# LangBot Workspace 多用户与 SaaS 多租户架构 状态:`ARCHITECTURE BASELINE — isolation kernel implemented; SaaS activation gates remain` 本文描述 Cloud v2 的目标架构和安全边界。详细的 Runtime、Box、PostgreSQL、pgvector 与 stdio MCP 决策以 [pending-architecture-decisions.md](./pending-architecture-decisions.md) 为权威来源;已经落地的实现选择记录在 [implementation-decisions.md](./implementation-decisions.md)。 “隔离内核已实现”仅表示开源 Core/SDK 已具备多租户数据和运行时隔离所需的基础能力, 不表示闭源控制面、计费、生产部署或 Cloud v2 已经可以上线。 ## 1. 架构决策摘要 Cloud v2 采用以下模型: > SaaS 对外只有一个逻辑 LangBot 实例,全部 Workspace 都是该实例内的租户; > 开源 Core 提供完整隔离内核,闭源 Cloud Control Plane 管理 SaaS 目录、订阅、权益和计费。 核心决策如下: 1. `Workspace` 是数据、成员、权限、用量和不可信执行的租户边界,不是一个 Pod、namespace、数据库或独立 LangBot 部署。 2. SaaS 注册 Account 时自动创建个人 Workspace;这只新增目录与业务记录,不创建租户专属服务、数据库、队列或 Runtime。 3. OSS 每个 LangBot 实例只能存在一个 Workspace,但该 Workspace 可以有多个 Account、邀请和固定角色。 4. SaaS 才允许一个 Account 拥有或加入多个 Workspace,并在 WebUI 中切换当前 Workspace。 5. MVP 可以各运行一个 Core、Plugin Runtime 和 Box Runtime 进程;未来增加副本或 PostgreSQL shard 仍属于同一个逻辑实例的内部扩展,不改变产品模型和外部 API。 6. 一个共享 Plugin Runtime 控制面管理所有 Workspace,但每个运行中的 plugin installation 独占一个 nsjail 进程;enabled-resident 是 desired semantics,只读代码和依赖可按已验证摘要共享。 7. 一个共享 Box Runtime 管理所有 Workspace;首期符合 entitlement 的 Workspace 最多拥有一个持久 `global` 逻辑 sandbox,实际命令继续以 nsjail 子进程执行。 8. SaaS 业务数据使用 PostgreSQL shared schema、应用层 scope 与 RLS 双重隔离;pgvector 位于同一个业务数据库并作为 SaaS 默认向量后端。 9. stdio MCP 有独立实例开关,Cloud v2 首期强制关闭,不能由 Box availability 或套餐能力隐式开启。 10. 闭源 Control Plane 可以作为模块化单体复用现有账户、支付和运营能力,但历史 Cloud 的租户专属部署模型不进入新架构。 11. Workspace 创建、释放、export、单 Workspace restore 和在线迁移的具体流程仍待后续决策。 本轮重构的最高目标是: > 共享可信控制面和基础设施池,隔离不可信执行单元;减少独立部署和常驻组件,使新增 Account 或空 Workspace 的静态成本接近零。 减少组件数量不意味着合并安全边界。插件进程、sandbox、secret、可写文件和租户数据仍必须严格隔离。 ## 2. 范围与非目标 ### 2.1 本方案覆盖 - OSS 单 Workspace 多用户、邀请和固定 RBAC。 - SaaS 多 Workspace 账户、成员和 Workspace 切换模型。 - HTTP、WebSocket、API Key、Bot、Webhook、后台任务和内部调用的可信 Workspace 上下文。 - Bot、Pipeline、Provider、Knowledge、Plugin、MCP、RAG、Session、Storage 和 Monitoring 的租户隔离。 - Plugin Runtime 与 Box Runtime 的共享控制面和进程级隔离。 - SaaS PostgreSQL shared schema、RLS 与 pgvector 边界。 - 开源 Core 与闭源 Control Plane 的职责、协议和故障边界。 - 当前单副本运行和未来同一逻辑实例内横向扩展的兼容约束。 - 分阶段实施、激活门禁和验收策略。 ### 2.2 本方案不覆盖 - 兼容或原地升级历史 Cloud 的租户专属部署方案。 - 为每个 Workspace 创建独立服务、数据库、schema、role、bucket、PVC、队列或 Runtime。 - 当前阶段实现多副本调度、跨地域 active-active 或 PostgreSQL 在线分片迁移。 - 第一版自定义角色、SAML、SCIM 或企业离线授权。 - 第一版 Workspace 级 BYOK E2B WebUI 配置。 - Cloud v2 首期 stdio MCP。 - Workspace export、释放、单租户恢复和在线迁移的具体产品流程。 历史客户数据、账户和财务记录如需迁移,应单独立项;旧部署拓扑不作为本架构的设计约束。 ## 3. 术语与不变量 ### 3.1 术语 | 术语 | 定义 | | --- | --- | | Account | 登录主体。OSS 中是实例本地账户;SaaS 中是全局账户 | | Workspace | 逻辑 LangBot 实例内的租户,是资源、成员、权限、用量和不可信执行的首要边界 | | Membership | Account 与 Workspace 的关系,包含固定角色、状态和权限版本 | | Invitation | 邀请一个 Account 或邮箱加入 Workspace 的一次性凭证 | | Logical Instance | 对外唯一的 LangBot 服务与安全域,拥有稳定 `instance_uuid`,不等同于某个进程或 Pod | | Replica | Core、Plugin Runtime 或 Box Runtime 的短期内部运行副本,不是产品实体 | | Execution Generation | Workspace 执行所有权和撤销的单调代数,用于隔离旧任务、旧连接和故障转移 | | Billing Account | SaaS 付款主体,可以为一个或多个 Workspace 付费 | | Entitlement | Control Plane 签发、Core 与 Runtime 本地执行的功能和数值额度快照 | | Cloud Control Plane | 闭源 SaaS 控制面,管理全局身份、Workspace 目录、订阅、权益、计费和生命周期 | | LangBot Core | 开源数据面,执行 Bot、Pipeline、Plugin、MCP、RAG 等业务并实施最终授权与隔离 | 当前代码中的 `placement_generation` 字段在迁移完成前保留兼容;其架构语义和目标命名均为 `execution_generation`,不表达 Workspace 属于某个产品级部署单元。 ### 3.2 必须始终成立的不变量 1. SaaS 只有一个稳定 `instance_uuid`;所有副本共享该身份。 2. `replica_id`、`worker_id`、Pod 名称、进程地址和数据库连接地址都是短期运行信息,不能进入业务资源的永久主键或外部 URL。 3. `workspace_uuid` 是租户数据、任务、缓存、文件、日志、用量和运行时隔离的稳定键,也是未来内部路由与分片的候选键。 4. OSS 一个实例最多一个 Workspace;SaaS 才能激活多个 Workspace。 5. 一个 Workspace 可以有多个 Account;一个 SaaS Account 可以加入多个 Workspace。 6. 所有租户业务资源都具有非空 `workspace_uuid`,并使用 `(workspace_uuid, resource_uuid)` 定位。 7. Workspace 选择器只是路由输入,不是授权凭证;服务端必须重新验证 Account、Membership、资源所有权和权限。 8. API Key、Bot、Webhook、后台任务、Plugin 与 Box 调用从可信所有权或绑定派生 Workspace,不能信任调用方自报 scope。 9. SaaS 缺少有效 Workspace 上下文时必须失败关闭,不能回退到第一个、最近或 OSS 默认 Workspace。 10. Core 是资源访问、运行时授权和 entitlement 执行的最后一道边界;Control Plane 不同步代理每条消息或普通资源请求。 11. 一个不可信插件进程只能属于一个 installation;一个 sandbox/session 只能属于一个 Workspace。 12. execution generation 失效后,旧任务、连接、回调和副作用必须被拒绝。 13. 本地进程表、缓存和临时目录都可重建,不能成为 desired state、撤销状态或业务数据的唯一真相。 14. 创建空 Workspace 不启动插件 worker、sandbox 或租户专属常驻组件。 15. 未来横向扩展不能改变 Workspace UUID、外部 API、权限模型或隔离语义。 ## 4. 产品与部署模型 ### 4.1 SaaS 逻辑拓扑 ```mermaid flowchart LR User["Browser / API / Bot traffic"] --> Edge["SaaS Edge"] User --> CP["Closed Cloud Control Plane
directory + subscription + billing"] Edge --> Core["One logical LangBot instance
Core replica pool; MVP = 1"] CP -->|"signed manifest, directory projection,
entitlement and desired state"| Core Core -->|"usage outbox and observed state"| CP Core --> PG["Shared PostgreSQL business database
RLS + pgvector"] Core --> PluginRT["Shared Plugin Runtime
trusted supervisor"] Core --> BoxRT["Shared Box Runtime
trusted supervisor"] PluginRT --> PluginA["Workspace A installation
isolated nsjail process"] PluginRT --> PluginB["Workspace B installation
isolated nsjail process"] BoxRT --> SandboxA["Workspace A
persistent global logical sandbox"] BoxRT --> SandboxB["Workspace B
persistent global logical sandbox"] Core --> ObjectStore["Shared durable object storage
Workspace-scoped keys"] ``` 这里的“一个逻辑实例”是一个服务、安全域和稳定身份,不是“永远只有一个 OS 进程”。 MVP 不实现分布式,但从第一天保留内部扩展所需的身份、幂等、generation 和 owner 抽象。 ### 4.2 容量演进 | 阶段 | 内部部署形态 | 新 Workspace 静态成本 | 启用条件 | | --- | --- | --- | --- | | M0 单副本 MVP | 一个 Core、一个共享 Plugin Runtime、一个共享 Box Runtime、一个 PostgreSQL business database | 只新增目录和业务行 | 当前目标 | | M1 同逻辑实例横向扩展 | 按容量增加 Core/Runtime 副本;使用 owner lease、fencing 和 generation;PostgreSQL 可增加 shared shard | 不创建 Workspace 专属部署 | 出现容量或可用性证据后 | | M2 Dedicated 资源等级 | 特定 workload 使用独享 worker pool、sandbox class 或 database shard,但沿用相同身份、协议和 schema | 仅购买该等级的客户承担 | 合规、驻留或超大负载需求 | M1 是 M0 的透明扩容,M2 是相同架构下的资源等级。外部 API 只认识稳定的 `instance_uuid` 和 `workspace_uuid`,不认识 replica、worker、pool 或 shard。 ### 4.3 当前不做分布式时必须预留的能力 1. 运行时协议携带稳定 `instance_uuid`、`workspace_uuid` 和 `execution_generation`,不依赖进程地址表达身份。 2. Plugin installation 和 Box session 使用稳定 owner 抽象;启用第二个副本前再实现带 expiry、CAS 和 fencing token 的 lease。 3. 创建、重试、回调、worker 注册和 outbox 使用稳定 idempotency key,重复投递不能产生第二个 owner 或副作用。 4. Repository/UoW 不允许无边界跨 Workspace 事务;`workspace_uuid` 可直接作为未来 shard key。 5. schema migration、后台任务扫描、监控聚合和运维接口不能假设永远只有一个 Core 进程。 6. Runtime 重启通过 durable desired state reconciliation 恢复,不依赖原进程或本地 cache。 7. 只有出现容量、可用性、地域或合规证据后才增加副本、lease store 或 shard router;预留协议不等于提前部署组件。 ### 4.4 组件边界 - Core、Plugin Runtime 和 Box Runtime 可以由发布与故障域决定是否共用 Pod,但必须保持独立进程身份、容器和 security context。 - Core 不能继承 nsjail、cgroup 或 mount namespace 所需的高权限。 - Plugin Runtime 与 Box Runtime 不合并为一个高权限进程。 - MVP 不新增 Runtime 专用数据库、Box 专用数据库、Kafka、Redis、租户级 scheduler 或 artifact service。 - 可信 supervisor、数据库连接池、只读 artifact cache 和基础容量可以多租户共享。 ## 5. OSS 与 SaaS 产品行为 ### 5.1 能力矩阵 | 能力 | OSS | SaaS | | --- | --- | --- | | Workspace 数量 | 实例固定一个 | Account 可拥有或加入多个,受 ProductPolicy 约束 | | Workspace 成员 | 多用户 | 多用户,受 entitlement 约束 | | 邀请成员 | 支持 | 支持 | | 固定 RBAC | 支持 | 支持 | | 自定义角色 | 不支持 | 后续商业能力 | | Workspace 创建 | 首次初始化创建唯一 Workspace | 注册自动创建个人 Workspace;后续创建受 ProductPolicy 约束 | | Workspace 切换 | 无需展示 | 支持 | | 订阅与计费 | 无远端依赖 | 闭源 Control Plane 管理 | | 租户隔离 | 完整实现 | 完整实现 | OSS edition policy 应表达为: ```text workspace_limit = 1 members_enabled = true invitations_enabled = true fixed_rbac_enabled = true multi_workspace_enabled = false ``` 不能用 `member_limit = 1`、关闭邀请或移除 RBAC 来实现单租户限制。 ### 5.2 OSS 初始化和邀请 首次初始化在一个事务中完成: 1. 创建本地 Account。 2. 创建实例唯一 Workspace。 3. 创建 owner Membership。 4. 创建默认 Pipeline、metadata 等 Workspace 初始资源。 5. 标记实例初始化完成。 初始化后默认关闭公开注册。后续用户由 owner/admin 创建一次性 Invitation,注册或登录后接受邀请并加入唯一 Workspace。 OSS 后续注册不创建第二个 Workspace。未配置 SMTP 时,系统返回只展示一次的邀请链接供管理员通过可信渠道发送。 ### 5.3 SaaS 注册和邀请 普通注册由 Control Plane 通过幂等工作流完成: 1. 创建或确认全局 Account 与 AuthIdentity。 2. 创建 personal Workspace 和 owner Membership。 3. 创建初始 Subscription/Entitlement 投影。 4. 完成 verified email、速率限制和基础风控。 5. 将 Account、Workspace 和 Membership 投影到 Core。 6. Core 达到要求的目录 revision 后返回可访问 route。 注册只创建逻辑记录,不启动 Runtime 或租户专属基础设施。 通过邀请注册的新用户也创建自己的 personal Workspace,同时加入受邀 Workspace;已注册用户接受邀请时只新增目标 Membership。 个人 Workspace 与团队 Workspace 的付费关系必须由 ProductPolicy 明确,不允许代码根据名称或创建路径隐式推断。 ### 5.4 Invitation 安全规则 - token 使用至少 256-bit 加密安全随机数,数据库只保存 hash。 - token 具有 `expires_at`、`accepted_at`、`revoked_at`,只能使用一次。 - Membership 创建与 token 消费在同一事务中提交。 - Invitation 不能授予 owner;owner 转移使用独立流程。 - SaaS 接受邀请时必须验证目标邮箱;OAuth 邮箱相同不能跳过 token 和显式确认。 - Workspace 必须始终至少有一个 active owner;admin 不能移除或降级 owner。 - 浏览器邀请链接把 secret 放在 URL fragment 中,页面读取后立即清除 fragment,并只短期保存在 `sessionStorage`。 ### 5.5 固定 RBAC Core 权威定义 `owner`、`admin`、`developer`、`operator` 和 `viewer` 固定角色。 权限按能力划分,例如资源查看、资源管理、运行操作、成员管理、provider secret 管理、审计查看和数据导出。 规则: - 普通资源可见性不自动授予 secret 可见性。 - 跨 Workspace 猜测资源 UUID 返回 404,不泄露存在性。 - 同 Workspace 资源存在但缺少权限时返回 403。 - 最后一个 owner 不能被删除或降级。 - 前端隐藏或禁用无权限入口只改善体验;后端仍必须执行所有授权检查。 ## 6. 开源与闭源职责边界 ### 6.1 LangBot Core OSS Core 负责: - 本地 Account、Workspace、Membership 和 OSS Invitation。 - 固定 RBAC 与单 Workspace edition policy。 - 业务资源及其 Workspace scope。 - HTTP、WebSocket、后台任务和运行时请求上下文。 - Plugin、MCP、RAG、Box、Session、Storage 和 Monitoring 隔离。 - SaaS Account/Workspace/Membership 的版本化执行投影。 - InstanceManifest、EntitlementSnapshot 和 Runtime 控制通道验证。 - 通用 capability 与数值 quota enforcement。 - UsageEvent/business outbox 和基础安全审计。 Core 是 Bot、Pipeline、Model、Knowledge、Plugin installation、MCP configuration 和 Monitoring 数据的权威来源, 也是每个业务和运行时请求的最终授权边界。 ### 6.2 Closed Cloud Control Plane Control Plane 负责: - SaaS 全局 Account、AuthIdentity、Session、OIDC 和后续 SSO。 - SaaS Workspace、Membership 和 Invitation 的权威目录。 - Workspace 创建、暂停、归档和删除工作流。 - BillingAccount、Product、PlanVersion、Price、Subscription、Invoice、Refund 和 provider event。 - Entitlement 计算、签名与版本。 - Usage ledger、聚合、额度和欠费策略。 - 实例 manifest、release、capacity、内部 desired state 和 observed state。 - SaaS 运营后台、平台角色和高级审计。 首期不把这些职责拆成多个租户、计费和调度微服务。推荐以一个独立于 Core 的闭源模块化单体承载, 并通过模块边界复用已有账户、OAuth、支付、邮件和运营能力。历史 Cloud 的租户专属部署代码不复用。 Control Plane 不保存 Bot、Pipeline、Model 或 Knowledge 等业务内容,也不代理普通消息执行。 ### 6.3 SaaS Adapter Core 中只保留薄的协议适配层: - 验证 InstanceManifest、Account token 和 JWKS。 - 消费 DirectoryEvent 并写入本地投影。 - 缓存并验证 EntitlementSnapshot。 - 将 UsageEvent 写入 durable outbox。 - 接收 execution desired state 并上报 observed state。 适配层不得 monkey patch ORM、绕过 Core 权限检查或在普通资源请求中同步调用 Control Plane。 ### 6.4 Source of Truth | 数据 | OSS | SaaS | | --- | --- | --- | | Account、Workspace、Membership | Core 本地数据库 | Control Plane 权威,Core 保存版本化投影 | | Invitation | Core 本地数据库 | Control Plane 权威,不向 Core 投影 pending secret | | Bot、Pipeline、Model、KB、Plugin、MCP | Core | Core | | Subscription、Payment、Invoice、Usage ledger | 无远端依赖 | Control Plane | | Feature 和 quota | 本地 edition policy | Control Plane 签发,Core/Runtime 验证执行 | | Execution generation | OSS 固定本地值 | Control Plane desired state,Core 执行 | | 运行时授权 | Core | Core 根据本地投影和 entitlement 执行 | SaaS 不维护两套可写目录。Control Plane 是目录权威写模型;Core 只保存带 revision 的执行投影。 ## 7. 控制面协议 ### 7.1 InstanceManifest 仅设置 `system.edition=cloud`、环境变量或前端 feature flag 不得启用 SaaS 多 Workspace。 Cloud bootstrap 必须验证由预置根信任签名的 InstanceManifest,并据此安装闭源 Workspace policy。 Manifest 至少绑定: ```text iss, aud, sub, jti, iat, nbf, exp instance_uuid release capabilities tenant_isolation_version execution_generation delegated issuers and keyset revision ``` 签名错误、audience 不匹配、过期、generation 回滚或信任链缺失时必须失败关闭,不能降级为 OSS 默认 Workspace。 ### 7.2 DirectoryEvent 与目录新鲜度 Control Plane 通过 transactional outbox 发布 Account、Workspace 和 Membership 的版本化事件。 Core 使用 inbox 按 `event_id` 去重,以 aggregate revision 拒绝旧写,并追踪连续应用水位。 要求: - 事件和 batch 经过实例绑定的强认证与签名。 - 重复、乱序、延迟、断流和全量 replay 都安全。 - 删除使用 tombstone。 - 新实例先导入带 high watermark 的 snapshot,再消费增量。 - projection 未就绪或落后于授权 lease 要求时,交互与自动化请求按策略失败关闭。 - SaaS pending Invitation、email 和 token hash 不进入 Core 投影。 MVP 可采用一个共享、原子且可恢复的 Control Plane store;未来多副本不能继续使用进程内状态承担一次性 token 或目录水位。 ### 7.3 EntitlementSnapshot Entitlement 使用版本化签名快照,至少绑定: ```text instance_uuid workspace_uuid plan_revision entitlement_revision status features limits nbf, exp, grace_until ``` Core 校验 issuer、audience、subject、instance、revision、时间和签名;旧 revision 不覆盖新快照。 套餐名称和价格规则只存在于闭源 Control Plane,Core 与 Runtime 只理解通用 capability 和数值限额。 Control Plane 故障时,已缓存且仍有效的快照可继续执行;过期后只能进入明确、有限的 grace 模式或失败关闭。 ### 7.4 UsageEvent 与 outbox 用量事件 append-only、至少一次投递,Control Plane 按 `event_id` 去重。事件至少包含: ```text event_id instance_uuid workspace_uuid execution_generation meter quantity_integer unit source occurred_at entitlement_revision schema_version ``` Core 不计算账单金额,也不在普通请求中同步扣费。业务写入与相应 business outbox 必须在同一事务中提交; generation-aware write fence 与 outbox 原子性尚是 SaaS 激活门禁。 ### 7.5 Desired state 与 observed state 闭源控制面发布版本化的 release、capacity 和 execution desired state,Core/Runtime 幂等 reconcile 并上报 observed state。 desired state 只描述同一逻辑实例内部的执行所有权和容量,不产生新的产品级实例或租户实体。 Workspace 安全状态由 directory revision 决定,订阅状态由 entitlement revision 决定,执行撤销由 execution generation 决定。三者取最严格有效状态,但任何通道都不能修改另一个通道的权威字段。 ## 8. 身份、鉴权与请求上下文 ### 8.1 上下文模型 租户业务入口统一解析不可变的 `RequestContext`: ```python @dataclass(frozen=True) class RequestContext: instance_uuid: str workspace_uuid: str execution_generation: int principal_type: str principal_uuid: str permissions: frozenset[str] auth_method: str entitlement_revision: int | None request_id: str ``` 不同入口的 Workspace 来源: | 入口 | Workspace 来源 | | --- | --- | | Browser Account token | `X-Workspace-Id` 只作候选;服务端校验 Membership | | API Key | key 记录绑定的 Workspace,忽略 caller selector | | Public Bot / Webhook | Bot 或 webhook route 的可信所有权 | | Background job | durable payload 中的完整 scope,执行前重新验证 generation | | Plugin Host API | 认证控制连接和 immutable action context | | Box operation | 已验证 entitlement、admission grant 和 Runtime namespace | | System operation | 显式、最小能力的 SystemContext,禁止隐式全局上下文 | 禁止从模块全局变量、进程默认 Workspace、请求 payload 或“第一个 Workspace”推断 scope。 ### 8.2 Account token 与 Workspace discovery - 新 JWT 使用稳定 Account UUID 作为 `sub`,并绑定 issuer、当前 `instance_uuid` audience 和 expiry。 - 账户级 Workspace discovery 是一个窄 bootstrap capability,只列出该 Account 的 active Membership,不能执行租户业务。 - multi-Workspace 模式下,tenant route 缺少 selector 必须拒绝;OSS singleton 模式可由 policy 选择唯一 Workspace。 - Account token 不直接证明任一 Workspace 权限;Membership 必须在服务端解析并验证状态与 revision。 ### 8.3 API Key、WebSocket 与长任务 - API Key 只持久化 hash,raw secret 仅返回一次;记录绑定 Workspace、固定 scopes、状态、expiry 和 creator。 - Dashboard WebSocket 在升级后认证,并在每条入站消息前重新验证 Account、Membership、权限、资源所有权和 generation。 - 长时间 LLM、MCP、Plugin 或 Box 调用在产生副作用或接受结果前再次校验 execution generation。 - 临时凭证交换绑定发起者、Workspace、instance 和 generation;其他 scope 查询返回与不存在相同的 404。 ### 8.4 错误语义 | 场景 | 语义 | | --- | --- | | 未认证或 token 无效 | 401 | | 同 Workspace 资源存在但权限不足 | 403 | | 资源不存在或属于其他 Workspace | 404 | | edition / entitlement / quota 禁止 | 稳定领域错误码,不伪装为 500 | | execution generation 过期 | fail closed,并停止旧运行态 | | 未处理异常 | 稳定 `internal_error` + request ID;细节只进入服务端日志 | ## 9. Core 数据模型 ### 9.1 Account、Workspace 与 Membership 核心实体至少包含: ```text Account uuid email_normalized display_name status auth bindings Workspace uuid name status source: local | cloud_projection directory_revision WorkspaceExecutionState workspace_uuid instance_uuid execution_generation status write_fenced_at revision WorkspaceMembership workspace_uuid account_uuid role status directory_revision ``` 约束: - Membership 对 `(workspace_uuid, account_uuid)` 唯一。 - Workspace 的 source 不允许通过可变本地配置从 local 升级成 cloud projection。 - Cloud projection 只有在 manifest、instance binding、目录 revision 和 execution state 均有效时才可路由。 - OSS bootstrap 只创建或修复 local singleton Workspace。 ### 9.2 Invitation OSS Invitation 存在 Core 本地数据库;SaaS Invitation 只存在于闭源目录。 ```text WorkspaceInvitation uuid workspace_uuid email_normalized role token_hash expires_at accepted_at revoked_at created_by ``` 数据库约束必须保证同一 Workspace 与邮箱只有一个有效邀请,并保证 token hash 全局唯一。 ### 9.3 业务资源 所有租户资源显式包含 `workspace_uuid`,包括但不限于: - Bot、Pipeline、Provider、Model、Knowledge Base 和 vector record。 - Plugin installation、MCP configuration、API Key 和 webhook binding。 - Query、Message、Session、Monitoring、Usage 和 AuditEvent。 - Upload、ObjectRef、Skill、Runtime desired state 和 temporary credential session。 唯一键、索引、缓存 key、object key、日志维度和幂等键都必须包含 Workspace scope。 服务层不得暴露可绕过 Workspace 条件的普通 `get(id)`、`list()` 或 `delete(id)`。 ### 9.4 防御性约束 - tenant table 的 `workspace_uuid` 非空并有外键。 - SaaS PostgreSQL 关键表启用并强制 RLS。 - 需要全局唯一的 opaque token 使用 hash 唯一索引,不依赖 Workspace 内唯一。 - owner 保底、Membership revision、invitation one-shot 等规则同时由 service 和数据库事务保护。 - 任何跨 Workspace 运维操作必须走显式受审计的 system capability,不得复用普通 repository。 ## 10. PostgreSQL、pgvector 与存储 ### 10.1 数据库边界 - OSS 继续默认 SQLite,并可显式选择自托管 PostgreSQL。 - SaaS 使用一个 PostgreSQL business database、一个 `public` shared schema 和共享连接池。 - 创建 Workspace 不创建 database、schema、role 或专属连接池。 - 每个 tenant transaction 使用 `SET LOCAL` 建立 scope,并由统一 TenantUnitOfWork 保证 context 与 SQL 使用同一事务和连接。 - 应用层 Workspace scope 是第一道边界,`ENABLE` + `FORCE ROW LEVEL SECURITY` 是第二道边界。 - runtime role 必须是非 owner、最小权限、无 superuser、无 `BYPASSRLS`、无 role membership 和跨 schema 权限。 - schema、extension、policy 和 ACL 只由独立 release migrator 创建与验证;Cloud runtime 不执行 DDL。 - PostgreSQL 仅承载业务数据和 pgvector,不成为 Plugin/Box 通用协调数据库、进程目录或新的控制面数据库。 首期 migrator 和 runtime URL 必须连接同一个 host、port、database,但使用不同 role。 生产部署还必须证明 runtime credential 无法连接 PostgreSQL 集群中的其他 database;专用 endpoint 或经验证的 HBA/proxy 隔离仍是激活门禁。 ### 10.2 Transaction 与后台任务 - 一个 TenantUnitOfWork 只绑定一个 Workspace、一个 execution generation 和一个事务所有者任务。 - 子任务不能继承并提交、回滚或关闭父任务的 tenant session。 - 长时间 LLM 或网络等待不持有数据库连接;每次数据库 helper 打开短事务。 - detached task 只在父事务提交后启动,并自行建立新 scope;父事务回滚时取消待启动任务。 - generation-aware write fence 必须保持到 commit,并与 business outbox 原子提交;该能力完成前不得激活 SaaS 写流量。 ### 10.3 pgvector - SaaS 默认使用同一业务 PostgreSQL 中的 pgvector,不静默回退到 Chroma。 - 向量身份至少为 `(workspace_uuid, knowledge_base_uuid, vector_id)`。 - 向量操作使用相同 tenant context 与 RLS 契约。 - embedding 维度显式存储和校验;不匹配时失败关闭,不截断、补齐或改用无界扫描。 - extension、表、constraint 和 ANN index 由 release migration 创建。 - OSS 默认仍可使用 SQLite + Chroma;选择 pgvector 时遵守相同 scope。 ### 10.4 Object storage - 大对象、plugin artifact、upload、knowledge 文件和 sandbox 文件不作为 PostgreSQL blob 存储。 - durable object key 和 metadata 都包含 Workspace scope;临时 staging 可包含 generation,但稳定业务引用不能因未来 generation 切换而永久失效。 - 现有 generation-scoped opaque key 在固定 generation 的 OSS 中安全,但 Cloud cutover 前必须实现稳定 final identity 或原子引用迁移。 - public image 与 private document 使用不同 capability;不能把通用 upload key 当作公开读取凭证。 ## 11. Plugin Runtime ### 11.1 共享 supervisor、独立 worker 整个逻辑实例共享一个可信 Plugin Runtime 逻辑控制面;M0 由一个 supervisor replica 承担。新 Workspace 不创建专属 Runtime、连接、卷或进程。 每个运行中的 plugin installation 独占一个 nsjail worker process tree;enabled-resident 是 desired semantics。worker 运行期间永久绑定: ```text instance_uuid workspace_uuid execution_generation installation_uuid runtime_revision artifact_digest ``` 插件不能通过 payload、Host API 参数、环境变量或重连改变该绑定。Supervisor 不在自身解释器中加载第三方插件代码。 停用、删除、revision/generation 变化或 entitlement 撤销时,旧 worker 必须停止并失去 Host API 权限。 ### 11.2 文件和进程边界 ```text data/plugin-runtime/ ├── artifacts/sha256//code/ # 已验证、只读共享 ├── environments/sha256// # 原子发布、只读共享 └── installations// ├── home/ # 私有可写 ├── tmp/ # 私有可写 └── data/ # 私有持久数据 ``` - 同插件同版本只有在 package digest 完全相同且完整性已验证时才共享只读代码。 - dependency environment key 包含 artifact/requirements digest、Python ABI、Runtime version 和 installer schema。 - installation 进程、配置、secret、home、tmp、data 和日志永不合并。 - namespace、private `/proc`、mount、PID、IPC、UTS、cgroup 与 rlimit 阻止读取其他文件、枚举或 signal 其他进程。 - Cloud 不从 artifact 自动加载 `.env`;secret 只由可信控制面按 installation 注入。 - 插件 egress 必须阻止访问 Core loopback、Box Runtime、数据库和平台 metadata endpoint。 ### 11.3 统一资源上限 资源限制只来自实例级 `data/config.yaml`,并支持现有环境变量覆写;plugin manifest 不能声明、放宽或覆盖。 ```yaml plugin: worker: max_cpus: 1.0 max_memory_mb: 512 max_pids: 128 max_open_files: 256 max_file_size_mb: 512 require_hard_limits: true ``` CPU、内存和 PID 使用 cgroup 硬限制,open files 和单文件大小使用 rlimit。 Cloud deployment profile 强制 nsjail;硬限制不可用时 readiness 失败,不能降级为普通子进程。 installation 总磁盘配额需要可原子拒绝写入的 quota provider,不能以目录扫描冒充硬限制。 ### 11.4 Desired state 与恢复 - PostgreSQL 中的 installation desired state 与 durable binary storage 是权威状态。 - Runtime 本地进程表、nsjail 目录、artifact/venv cache 都可重建。 - Runtime 重连执行实例范围 full reconciliation,清理 stale worker 并恢复 enabled installation。 - dependency preparation 失败记录在对应 installation,不启动半就绪 worker,也不阻塞其他 installation。 - desired semantics 要求 enabled installation 常驻,不做 idle eviction;是否按负载回收以后再决定。 - 当前 Supervisor 可在 Runtime 重连或 Core apply/reconcile 时恢复 desired state,但尚未为意外退出的 worker 实现带有界 backoff 的 completion callback;这项可靠性缺口在 Cloud 激活前必须补齐并验证。 真实 Linux/nsjail/cgroup 与受控 egress 的 Cloud 部署验证尚未完成,是生产激活门禁。 ## 12. Box Runtime 与 stdio MCP ### 12.1 共享 Box 控制面 整个逻辑实例共享一个可信 Box Runtime 逻辑控制面;M0 由一个 Runtime replica 承担。Core 与 Runtime 控制通道绑定稳定 instance identity, 每个 operation 绑定 `workspace_uuid`、`execution_generation`、session revision 和短期 admission grant。 首期 entitlement 模型: ```json { "features": { "managed_sandbox": true, "external_sandbox": false }, "limits": { "managed_sandbox_sessions": 1 } } ``` 闭源订阅模块把套餐映射为该通用 capability;Core 与 Runtime 不判断 `plan == pro`。 预期 Pro 得到 `managed_sandbox_sessions = 1`,其他套餐为 `0`。 ### 12.2 Sandbox 模型 - 合资格 Workspace 首次使用时懒创建一个持久 `global` 逻辑 session。 - `global` 表示 Workspace 内默认逻辑 sandbox,不表示跨 Workspace 共享。 - session TTL 不自动回收;Runtime 重启后进程和临时目录失效,但 `/workspace` 持久数据保留。 - 每次普通命令在 Box Runtime 容器内启动一个 one-shot nsjail 子进程。 - 首期禁止 managed background process 和 network,避免 session 被当成常驻共享主机。 - Core 与 Runtime 通过认证 random-marker challenge 证明看到同一 durable volume,不能只比较路径字符串。 - 文件同步、attachment 和 skill mount 沿用现有 nsjail 机制,但所有 host path 解析必须由可信 Workspace context 派生并防止 symlink/path escape。 Cloud readiness 必须证明 cgroup、namespace、mount、Workspace/Skill/ephemeral byte quota 和 inode quota 均为硬限制。 当前普通 nsjail backend 不具备全部硬磁盘能力,因此 Cloud Box 应失败关闭,直到绿地部署提供并验证真实 quota provider; 不能把软目录扫描写成“生产已就绪”。 ### 12.3 外部 E2B 非 Pro 用户后续可在 WebUI 配置 Workspace 自有的远程 E2B sandbox。该功能尚未实现,首期不纳入。 未来 credential 必须属于 Workspace、加密存储且读取受 secret 权限保护,不消耗 Cloud managed sandbox 配额。 ### 12.4 stdio MCP 独立开关 ```yaml mcp: stdio: enabled: true ``` - OSS 默认 `true` 保持兼容。 - Cloud v2 通过 `MCP__STDIO__ENABLED=false` 强制关闭。 - 该 gate 独立于 `box.enabled`、managed sandbox entitlement 和 session quota。 - gate 同时覆盖 create、update、test、bootstrap load 和最终 Runtime execution。 - 已有 stdio 配置在 gate 关闭时保留但不启动,并返回明确的 feature-disabled 错误。 - HTTP/SSE 等远程 MCP transport 不受影响。 ## 13. HTTP API 与 WebUI ### 13.1 Core API OSS 与 SaaS 执行面共用通用 Workspace API: ```text GET /api/v1/workspaces GET /api/v1/workspaces/{workspace_uuid} GET /api/v1/workspaces/{workspace_uuid}/members POST /api/v1/workspaces/{workspace_uuid}/invitations PATCH /api/v1/workspaces/{workspace_uuid}/members/{account_uuid} DELETE /api/v1/workspaces/{workspace_uuid}/members/{account_uuid} ``` Cloud policy 下,目录 mutation 由闭源 Control Plane 负责;Core 对本地创建、邀请和成员修改返回稳定的 `control_plane_required`,只提供执行投影的安全读取。 所有 tenant resource route 必须经过统一 decorator/middleware: 1. 认证 principal。 2. 解析可信 Workspace。 3. 校验 Workspace/ExecutionState。 4. 校验 Membership 或资源绑定。 5. 校验 permission 和 entitlement。 6. 创建 RequestContext 与 TenantUnitOfWork。 ### 13.2 SaaS Control Plane API SaaS 产品 API 包含: ```text POST /cloud/workspaces GET /cloud/workspaces POST /cloud/workspaces/{workspace_uuid}/invitations POST /cloud/invitations/{token}/accept GET /cloud/workspaces/{workspace_uuid}/subscription POST /cloud/workspaces/{workspace_uuid}/checkout GET /cloud/workspaces/{workspace_uuid}/usage ``` 这些 API 管理目录、产品和计费,不直接操作 Bot/Pipeline 等 Core 业务资源。 ### 13.3 WebUI OSS: - 首次注册进入唯一 Workspace。 - owner/admin 可邀请成员并管理固定角色。 - 不展示 Workspace 切换器和创建第二 Workspace 的入口。 SaaS: - 登录先获取 Account 级 Workspace 列表,再显式选择当前 Workspace。 - 当前 Workspace UUID 保存在受控客户端状态中;所有 tenant request 自动附带 selector。 - 切换 Account 或 Workspace 时清理缓存、WebSocket、上传、表单、错误和 optimistic state,不能显示前一租户数据。 - 页面 refresh、新 tab 和邀请跳转恢复同一个经过授权的 Workspace;失效 Membership 不回退到其他 Workspace。 - UI 权限变化必须响应式更新,但 API 仍是最终授权边界。 ## 14. 故障、安全与降级 ### 14.1 Fail-closed 场景 以下情况必须拒绝新的租户业务和副作用: - Cloud manifest 缺失、签名失败、audience 错误或回滚。 - Account token、Membership、Workspace status 或 execution generation 无效。 - 目录投影未就绪或落后于有效 lease 要求。 - Entitlement 缺失、过期且不在明确 grace 范围内。 - Runtime 控制通道认证失败或实例绑定不一致。 - Plugin nsjail/cgroup hard limit 在 Cloud profile 下不可用。 - Box 的任一硬存储或 namespace capability 无法证明。 - PostgreSQL RLS、runtime role、schema、catalog 或 endpoint 隔离校验失败。 - stdio MCP 在 Cloud profile 下被尝试启用。 不能把上述错误静默降级为 OSS singleton、普通子进程、Chroma、软 quota 或 caller-supplied Workspace。 ### 14.2 撤销语义 - Membership 删除或降权必须影响下一次 HTTP 请求,并使长连接在下一条消息前重新授权。 - Workspace 暂停禁止新交互、自动化工作负载和新副作用;恢复只允许当前 generation。 - entitlement 到期按 capability 明确停止新创建或新执行,不隐式删除已有数据。 - generation 变化使旧 worker、session、callback、cached runtime object 和 outbox publisher 失效。 - 控制面暂时不可达时,只能在有效签名快照和本地投影允许的范围内继续;过期后失败关闭。 ### 14.3 安全清单 - 所有 identifier 使用不可猜 UUID,但不把随机性当成授权。 - 所有 token/secret 只存 hash 或加密值,raw secret 一次展示。 - 日志、trace、metric、cache 和 object key 都包含 Workspace 维度并过滤 secret。 - Provider、Bot、Plugin、MCP 配置的 read response 递归遮蔽 credential。 - Runtime control、debug、registration 和 attachment capability 分离,不能复用万能 secret。 - untrusted code 不访问 Core loopback、数据库、其他 Runtime、宿主文件系统或 metadata endpoint。 - bulk operation、后台扫描和 monitoring 聚合使用显式 tenant/system capability。 - 所有跨 Workspace 运维操作记录 principal、reason、scope、request ID 和结果。 ## 15. 实现状态与 SaaS 激活门禁 ### 15.1 已实现的隔离内核 当前分支已经实现或具备基础的部分包括: - OSS singleton Workspace、多 Account、Invitation 和固定 RBAC。 - trusted RequestContext、Workspace-scoped repository 和资源所有权检查。 - tenant-aware Plugin SDK protocol 与 Runtime installation binding。 - shared Plugin Runtime / Box Runtime 控制协议和 execution generation fence。 - stdio MCP 独立 gate。 - PostgreSQL shared schema、transaction-local scope、FORCE RLS 与 pgvector adapter。 - Cloud bootstrap 默认不可由普通配置激活,并对缺失安全能力失败关闭。 这些是代码能力边界,不等于完成闭源 SaaS 产品或生产部署验收。 ### 15.2 尚未完成的激活门禁 以下事项完成并取得真实环境证据前,不得宣称 Cloud v2 production-ready: 1. 闭源 Control Plane 的全局目录、注册、邀请、订阅、计费、entitlement 签发和签名 manifest bootstrap;横向扩展前 OAuth exchange 与目录投影还必须使用原子共享存储。 2. 普通业务写入贯穿 commit 的 generation-aware fence,以及与外部副作用同事务的 business outbox。 3. generation cutover 后稳定的 durable object identity 或原子对象引用迁移。 4. 所有 tenant-configurable outbound URL 的 SSRF 防护与 tenant-safe egress;Plugin Runtime 还需在真实 Linux/nsjail/cgroup v2 环境验证 namespace、资源限制和文件隔离。 5. Plugin Runtime 对意外退出的 enabled worker 实现 completion callback、有界 backoff 和自动恢复,并验证不会形成跨租户重启风暴。 6. Plugin installation data 的 production hard disk quota provider,能够在写入边界原子拒绝超额,不能以目录扫描代替。 7. Box Runtime 的 production hard quota provider,包括 Workspace、Skill、root/tmp/home 的 byte 与 inode quota;真实部署还必须在启动和重连时通过共享卷 marker challenge。 8. PostgreSQL runtime credential 的专用 endpoint 或 HBA/proxy 跨 database 隔离证明、生产 migration/rollback 流程,以及 legacy pgvector migration 失败后精确恢复 RLS/FORCE 并可安全重试的集成证据。 9. 闭源目录事件、lease、snapshot、entitlement 和 usage/outbox 的重放、断流与灾难恢复验证。 10. 真实浏览器多 Account/RBAC/邀请/刷新场景已完成;仍需生产 Runtime 重启、worker crash、断流、异常回滚和闭源 Control Plane 的 fault-injection 验收。 ### 15.3 有意暂缓的产品决策 - Workspace 创建后的休眠、释放、删除和保留策略。 - Workspace export 与单 Workspace restore。 - 非 Pro Workspace 的 BYOK E2B WebUI。 - 多副本 owner lease 的 store、TTL、fencing token 和转移顺序。 - PostgreSQL shard resolver、在线迁移和 dedicated shard 产品规则。 - artifact/cache 的签名来源、撤销、GC 和磁盘配额机制。 - custom roles、SSO、SCIM 和企业合规能力。 暂缓项不得被实现代码用隐式默认值提前固化。 ## 16. 实施顺序 ### Phase 0:契约和基线 - 固定术语、RequestContext、角色矩阵、edition policy 和错误语义。 - 建立升级备份、回滚和跨租户负向测试基线。 ### Phase 1:OSS tenancy kernel - Account、Workspace、Membership、Invitation。 - singleton bootstrap、多用户邀请、RBAC 和前端权限。 ### Phase 2:数据与入口隔离 - 为所有资源补充 Workspace scope。 - HTTP、API Key、Bot、Webhook、WebSocket、后台任务和 storage 统一上下文。 - SQLite migration recovery 与 PostgreSQL RLS 集成测试。 ### Phase 3:Runtime 与 SDK 隔离 - Plugin installation binding、nsjail、资源上限和 artifact replay。 - Box admission、session namespace、skill/attachment 文件边界。 - MCP gate、RAG/vector 与 long-running generation revalidation。 ### Phase 4:闭源 SaaS 控制面 - signed manifest bootstrap。 - 全局目录、注册、邀请、Subscription、Entitlement 和 Usage ledger。 - projection、lease、outbox、reconciliation 和运维后台。 ### Phase 5:生产部署激活 - 真实 Linux Plugin/Box hard isolation。 - PostgreSQL credential、migration、backup 和 rollback 验证。 - 完整浏览器/API/Runtime E2E 和故障注入。 - 所有激活门禁通过后才开启多 Workspace Cloud policy。 ### Phase 6:同逻辑实例内部扩展 - 有容量证据后增加副本、owner lease 和 fencing。 - 有地域、合规或规模证据后增加 shared/dedicated shard。 - 保持外部身份、API 和 Workspace URL 不变。 ## 17. 测试与验收 ### 17.1 数据隔离 - 两个 Workspace 使用相同 resource UUID、name、vector ID 和 cache key,不发生冲突或越权。 - 故意遗漏应用层 Workspace filter 时,PostgreSQL RLS 仍阻止跨租户读写。 - 连接池复用、异常回滚、子任务、后台任务和 transaction pooling 不残留 tenant context。 - 跨 Workspace 猜测返回 404;同租户缺权限返回 403。 ### 17.2 产品行为 - OSS 首个 Account 创建唯一 Workspace;第二个 Account 只能通过邀请加入;创建第二 Workspace 返回 edition error。 - 邀请覆盖有效、已使用、撤销、过期、邮箱不匹配和并发接受。 - owner/admin/developer/operator/viewer 的 API 和 WebUI 权限一致。 - SaaS 普通注册和邀请注册都创建个人 Workspace,但不创建专属部署或 Runtime。 ### 17.3 Runtime - 两个 Workspace 安装同一已验证 artifact 时只共享只读 code/env,进程、secret、home/tmp/data、日志和 Host API 完全隔离。 - cgroup、rlimit、namespace、egress 和 generation fence 在真实 Linux 环境生效。 - Runtime restart/cache loss 通过 durable desired state 与 binary storage 恢复。 - 两个 Workspace 的 Box session、files、process、skill、attachment 和 quota 完全隔离。 - stdio MCP gate 对 UI、API、bootstrap 和最终 execution 同时生效。 ### 17.4 Control Plane 与故障 - DirectoryEvent 重复、乱序、缺口、snapshot + replay 和过期 lease 均安全。 - Entitlement 旧 revision、签名错误、过期和撤销均失败关闭。 - UsageEvent 重放不重复计费;业务事务回滚不发送副作用。 - Runtime、Core 或 Control Plane 重启不创建重复 Workspace、worker 或 sandbox。 - manifest、数据库安全校验或 hard quota 缺失时实例保持不可激活,而不是静默降级。 ### 17.5 浏览器端到端 真实浏览器至少覆盖: 1. clean database 首位 owner 注册与 singleton Workspace bootstrap。 2. owner 创建邀请,第二个用户注册/登录并接受。 3. 角色在 viewer/operator/developer/admin 间变化时,导航、控制项和 API 结果同步变化。 4. Account/Workspace 切换清空前一 scope 状态,refresh 和新 tab 恢复正确 Workspace。 5. 第二 Workspace edition limit,以及 invitation used/revoked/expired/email mismatch 的可见错误。 6. 直接 API 越权、伪造 selector 和跨租户 UUID 猜测不能绕过 UI。 ## 18. 最终结论 Cloud v2 的产品模型只有一个逻辑 LangBot 实例和实例内多个 Workspace。 当前选择单副本 MVP 是为了减少组件和新增租户成本,不是把单进程假设写进业务身份或协议。 未来需要容量或高可用时,在同一逻辑实例内部增加 Core/Runtime 副本和 PostgreSQL shard, Workspace 的 UUID、权限、数据边界和外部 API 均保持不变。 开源 Core 必须完整实现安全的 Workspace 隔离和 OSS 单 Workspace 多用户;闭源 Control Plane 管理 SaaS 的全局目录、订阅、权益、计费和生命周期。共享可信控制面、连接池、只读 artifact 和数据库组件, 同时让每个不可信插件进程、sandbox、secret、可写文件和 tenant transaction 保持独占边界, 才能在不增加每租户部署的前提下最大化降低新增用户成本。 在闭源控制面、事务 fence/outbox、真实 Runtime hard isolation、Box hard quota 和 PostgreSQL 生产隔离等门禁完成之前, 本架构仍处于隔离内核阶段,不应被描述为可上线的 SaaS 多租户部署。