# Kuruwa:从原型到 MVP 的施工计划 ## 1. 目标与施工边界 本计划以 [第一版设计](docs/design-v1.md) 为功能依据,以 [领域词汇表](CONTEXT.md) 为术语依据。计划描述待实施工作,不代表相关功能已经实现。 MVP 的交付目标是:人工启动项目级主 daemon,配置多个租户入口和可信沙盒定义;租户在获准实例中使用 muxrun 式终端操作;人工能够交付一个高权限终端、通过原生 RMUX attach 干预,并随时撤回租户访问。 以下约束贯穿所有阶段: - 沙盒定义、沙盒实例和终端分别建模;命令任务与长期 shell 使用同一种终端模型。 - 每个实例有一个处于最终执行环境内的 RMUX,它自然派生终端进程。不引入双层 PTY 桥接。 - 初始化由可信外部程序执行,允许跨越 `exec`。主机 noop 和 bwrap 是示例定义,不是硬编码的策略类型。 - 租户身份来自其连接的受限入口,不接受请求自报身份作为授权依据。 - 创建终端必须显式指定实例,创建权随实例控制权授予。终端名称在一个主 daemon 内全局唯一,但名称不授予访问权。 - 客户端连接失败直接报错,不回退到主机,不启动替代 daemon。 - 公开接口实施授权;不宣称弱沙盒能够防止所有宿主资源旁路。 本计划不包含 agent 框架集成、嵌套租户委派、沙盒预制组合、任务恢复系统、跨主 daemon 名称协调、分布式服务或通用插件框架。不以引入数据库、独立权限服务或额外加密机制作为施工前提。 ## 2. 起点与现有证据 ### 已完成 - [x] 完成核心对象、权限关系及生命周期的设计访谈。 - [x] 完成 RMUX 0.8.0 监听 FD 接入的可丢弃原型。 - [x] 实测外侧预绑定 listener,经 bwrap 初始化后由 RMUX 导入并开始服务。 - [x] 实测 socket 路径隐藏、终端不继承内部通信 FD、typed SDK、原生 attach、实际退出状态、SIGUSR1 和清理。 原型及复现说明位于 [prototypes/rmux-listener-fd](prototypes/rmux-listener-fd/README.md)。当前复跑命令如下,前提是已有应用补丁并构建完成的上游源码目录: ```sh RMUX_SRC=/tmp/rmux-src/rmux prototypes/rmux-listener-fd/verify.sh ``` ### 尚未完成 生产主 daemon、租户入口、授权执行、实例管理、产品 CLI、配置加载及可发布的 RMUX 集成都未实现。原型依赖临时目录中的源码与构建结果,不能作为正式交付方式。 ## 3. 施工顺序 ```text M0:冻结最小接口契约 ↓ M1:整理 RMUX 适配与可复现构建 ↓ M2:主 daemon → 实例 → 终端的最小闭环 ↓ M3:租户入口、授权与撤回 ↓ M4:补齐 muxrun 式 CLI 与宿主 attach ↓ M5:生命周期、配置与完整演练 ↓ M6:MVP 验收与交付 ``` 每阶段满足退出条件后再进入下一阶段。阶段内尽量交付端到端的小切片,不先建设没有调用方的通用抽象。M0 完成后,RMUX 适配与主 daemon 的基础代码可以独立推进;端到端验收仍按上述依赖顺序进行。 当前阶段进度: | 阶段 | 状态 | 说明 | | --- | --- | --- | | M0 冻结最小接口契约 | 完成 | 契约见 [最小接口契约](docs/contracts.md)。 | | M1 RMUX 适配与构建 | 完成 | 固定源码构建、普通 listener 回归与继承 FD 真 PTY 验证均通过,见 [构建说明](docs/rmux-build.md)。 | | M2 实例与终端闭环 | 完成 | 管理入口与 host/bwrap 实例实测通过,`scripts/test-m2.py` 的 137 项断言通过。 | | M3 租户授权与撤回 | 完成 | 真实权限套件 812 项断言、后端阻塞回归 31 项、写出背压回归 16 项通过;撤回保留终端并切断旧访问。 | | M4 终端 CLI 与人工接管 | 完成 | 真实 CLI/PTY 套件 434 项断言通过,覆盖原生 attach、输入、resize、detach、取消失败及授权回归。 | | M5 生命周期与部署 | 完成 | `scripts/test-m5.py` 的 297 项断言通过,完整演练双租户、交付、原生接管、撤回、清理及重启;部署说明见 `docs/deployment.md`。 | | M6 MVP 验收与交付 | 完成 | 干净 checkout 构建与 `scripts/check.sh` 全阶段 `CHECK_PASS`;主 agent 在原仓库复跑 exit 0,日志 `/tmp/opencode/kuruwa-final-primary.log`。 | ## 4. M0:冻结最小接口契约 目的:补齐实现必需的参数和行为约定,不重新讨论已确认的架构。 ### 工作项 - [x] 写出 CLI 命令表,覆盖管理操作、实例操作和终端操作。明确入口参数与环境变量的优先级、实例参数、终端名称、标准输出与错误输出、退出状态。 - [x] 确定配置文件格式及最小字段:沙盒定义、初始化与清理程序、租户声明、获准创建的定义。运行实例与临时终端授权不得混入持久配置。 - [x] 定义初始化契约:监听 FD、最终 RMUX 启动方式、必要的实例信息、就绪判定、失败报告及清理程序的输入。不得在就绪前公布可用实例。 - [x] 明确单一终端模型下 `run/resume` 的等待、超时、读取和清理语义。保留 muxrun 的使用体验,但不要默默复制与单终端访问授权冲突的破坏性操作。 - [x] 明确名称占用、释放与重用规则,以及旧授权、旧连接如何绑定原对象而非后来同名对象。采用满足需求的最简单身份机制,不引入内容摘要或加密标识。 - [x] 建立操作授权表,逐项区分管理权、实例控制权和单终端访问权;特别列明 `resume`、`cancel`、`clean`、`gc` 的非只读行为。 ### 交付物 [最小接口契约](docs/contracts.md):命令语法与退出码、配置样例、初始化环境表、名称与身份规则、运行目录布局、权限矩阵、撤回语义,以及完整纸面命令演练。 ### 退出条件 通过纸面命令演练走完“启动主 daemon → 创建租户 → 创建实例 → 创建终端 → 交付访问 → 撤回 → 清理”。每一步都有确定的入口、目标对象、所需权限、成功结果和失败结果,无须实现者再猜测产品语义。演练记录在契约中,并明确标注为实现前契约。 ## 5. M1:整理 RMUX 适配与可复现构建 目的:把已经验证的机制变成项目能够稳定构建和调用的依赖,不扩大 RMUX 改动范围。 ### 工作项 - [x] 固定 RMUX 与 SDK 的兼容版本,通过项目现有环境和构建配置获取、应用补丁并构建;正式构建不得依赖 `/tmp/rmux-src` 已存在。 - [x] 整理继承 listener 的最小改动:所有权转移、监听 socket 校验、非阻塞设置、CLOEXEC、启动入口和错误退出。 - [x] 处理原型对 Linux 配置类型 `Clone`/`Eq` 的兼容性影响,明确本项目实际使用的接口。向上游提交补丁可以后续进行,不作为 MVP 前置条件。 - [x] 保留普通按路径启动的行为;继承 listener 时禁止按不可见路径重绑定或误清理其他 socket。 - [x] 将原型中必要的行为检查转成可重复的回归检查,保留原型作为验证来源。 ### 退出条件 从干净依赖源码构建成功。继承 FD 和普通路径启动均可用;非法 FD、非监听 socket 和启动失败有明确结果。导入后生成的终端不继承 listener 或已接受连接;SIGUSR1、退出清理和原生 attach 的行为有实测证据。 ## 6. M2:实现单实例、单终端的最小闭环 目的:首先让真实主 daemon 管理真实执行环境,不以模拟 RMUX 的结果替代集成。 ### 工作项 - [x] 提供显式启动和关闭项目主 daemon 的 CLI,建立管理入口和本次运行的对象登记。 - [x] 读取可信定义,通过外侧预绑定 listener 启动初始化程序,等待实例 daemon 就绪,再登记为可用实例。 - [x] 为每个实例连接其 RMUX,只使用连接已有服务的路径,不调用自动启动或自动替换服务的接口。 - [x] 建立全局终端名称登记及实例归属,实现最小的创建、状态、输出读取与清理。 - [x] 提供两个可运行示例:主机 noop 初始化,以及隐藏内部 socket 路径的 bwrap 初始化。它们调用同一初始化契约,不增加策略分支。 - [x] 处理初始化退出、就绪失败、终端创建失败和实例 daemon 退出;清理该次失败创建拥有的资源,不返回半成品对象。 ### 退出条件 经项目 CLI 在两个不同实例中运行命令,得到各自预期环境的输出。重复终端名称被拒绝,缺失实例不会导致主机执行。客户端断线后终端继续存在;实例 daemon 退出后实例被判定失效,后续操作不会自动新建替代实例。 ## 7. M3:实现租户入口、授权与撤回 目的:在开放完整操作面之前建立不可由请求参数绕过的授权路径。 ### 工作项 - [x] 由管理入口管理租户及其受限 socket。连接进入哪个入口,就使用哪个租户的授权上下文。 - [x] 实现实例创建授权、实例控制权和指定终端访问授权;不增加独立的创建终端权限。 - [x] 所有对象查找、列表、终端操作和生命周期操作经过同一授权语义。请求中猜出的全局名称不得绕过权限检查。 - [x] 把受限请求映射到允许的 typed SDK 操作,不开放任意 RMUX 命令或原始内部 endpoint。 - [x] 实现撤回:关闭相关受限连接与输出订阅,阻止后续访问,并保留终端供人工接管。不能只删除 socket 路径或等待客户端下次主动请求。 - [x] 对对象销毁与名称重用执行 M0 的身份规则,避免旧授权转移到同名新终端。 ### 退出条件 至少用两个租户及一个管理入口运行以下正反向测试: | 场景 | 必须观察到的结果 | | --- | --- | | 租户 A 使用获准定义创建实例 | 创建成功,A 可以管理该实例的终端。 | | A 请求未获准定义或其他租户实例 | 请求被拒绝,不产生执行副作用。 | | 管理端仅交付高权限终端 T 给 A | A 可以读取和输入,但不能创建同实例的新终端或销毁实例。 | | 租户 B 猜出 T 的名称 | B 不能访问 T,列表不暴露未授权终端。 | | A 伪造请求中的租户身份 | 仍按 A 的实际入口授权,不获得管理权限。 | | 管理端撤回 A 对 T 的访问 | 已有访问结束,后续访问失败,T 仍运行且管理端可用。 | | T 被销毁后其名称重新使用 | 旧授权和旧连接不能操作新对象。 | ## 8. M4:补齐终端 CLI 与人工干预 目的:达到 muxrun 已证明可用的日常操作能力,不制作新的终端 UI。 ### 工作项 - [x] 补齐 `start`、`run`、`resume`、`status`、`ls`。 - [x] 补齐 `send`、`send --key`、`screen`、`tail`、`transcript`。 - [x] 补齐按授权执行的 `cancel`、`clean`、`gc`。信号必须由正确执行环境中的终端后端作用于实际进程,不能把 namespace 内 PID 当作主机 PID 使用。 - [x] 提供宿主管理端 `attach`:按终端名称解析内部 endpoint 与 session,直接启动原生 RMUX 客户端。连接现存服务,不自动启动或替换服务。 - [x] 保证租户端不能通过 `attach`、透传参数或错误输出取得原始 RMUX 控制入口。 - [x] 提供 CLI 帮助与使用示例;若保留 `skill` 命令,其内容与实际命令一致。 ### 退出条件 脚本能够启动、等待、检查、输入、读取、取消和清理终端。验证正常退出、非零退出、等待超时及客户端断线;输出检查必须区分命令回显与真实结果。人工原生 attach 能输入、调整尺寸、detach,且 detach 后终端继续运行。租户入口拒绝宿主专属操作。 ## 9. M5:闭合生命周期、配置与典型流程 目的:将功能组合成可日常使用的系统,收尾范围只覆盖已确认的生命周期要求。 ### 工作项 - [x] 完成持久配置加载以及租户入口创建;启动失败不得留下可误用的半初始化入口。 - [x] 完成正常关闭的实例与资源清理。临时实例、终端和高权限交付授权不在重启后恢复。 - [x] 完成非空实例拒绝销毁、显式强制销毁及自定义清理程序的调用;失败要能报告,而不是宣称清理成功。 - [x] 确认主机实例清理只涉及受管进程,客户端断线与权限撤回都不会误触发实例销毁。 - [x] 编写标准弱隔离示例的部署说明:定义写保护、租户入口暴露、内部入口隐藏,以及主机 shell 的真实权限边界。 - [x] 演练实例 daemon 异常退出与主 daemon 重启。保证旧对象不会静默指向新对象;崩溃孤儿自动回收仍不作为本阶段要求。 ### 退出条件 完整走通下面的用户流程,并记录每一步实际 CLI 命令和结果: 1. 人工启动主 daemon,并配置租户 A、B 与一个标准定义。 2. A 创建标准实例和两个终端;A 断线后任务继续运行,B 不能越权访问。 3. 人工创建高权限实例与终端 T,仅授予 A 对 T 的访问。 4. A 向 T 发送输入,人工通过原生 attach 观察并干预;不要求输入协调。 5. 人工撤回交付,A 失去已有和后续访问,T 仍可由人工接管。 6. 非空实例销毁被拒绝;显式强制销毁后相应终端和实例 daemon 结束。 7. 正常关闭、重新启动主 daemon,持久配置仍可使用,临时交付没有恢复。 ## 10. M6:MVP 验收与交付 ### 交付物 - [x] 可从干净工作目录构建的主 daemon、CLI 及适配后的 RMUX 依赖。 - [x] 配置样例、初始化契约说明、noop 与 bwrap 示例,以及启动和关闭说明。 - [x] 覆盖核心权限矩阵与生命周期的自动化行为检查。 - [x] 一条有文档说明的完整验收入口,能够运行 M5 的用户流程并清理测试资源。 - [x] 已知限制说明:Linux 验证范围、弱隔离旁路、运行态不恢复、原生 attach 的宿主管理权限,以及不保证崩溃孤儿自动回收。 ### 验收证据 - 干净 checkout:`21570bc` 导出目录运行 `scripts/check.sh`,exit 0,最终 `CHECK_PASS`;日志 `/tmp/opencode/kuruwa-m6-clean.log`。各阶段覆盖固定 RMUX 构建/回归、cargo 格式/构建/单测/clippy、M2–M5 真实套件和继承 FD 验收。 - 主 agent 独立审计:确认 `check.sh` 与干净目录一致、`src` 一致,并在原仓库复跑 `scripts/check.sh`,exit 0,最终 `CHECK_PASS`;日志 `/tmp/opencode/kuruwa-final-primary.log`。 ### 验证纪律 测试通过真实 CLI 或对外接口触发行为,并断言具体结果。权限测试必须同时包含合法请求成功和越权请求失败;不可仅检查函数调用次数或输入文本。FD 和 attach 测试使用真实进程、socket 与 PTY。 在项目任务入口中提供格式检查、构建、单元测试和端到端检查。具体命令随代码落地记录,不在当前计划中虚构已经存在的测试命令。受环境权限阻碍的测试应标记为受阻,并列出所需环境,不计为通过。 ### 完成条件 M1 至 M5 的退出条件全部有实际证据,M6 交付物齐全,且首次使用者能够按说明从干净环境完成标准流程,即达到 MVP。此时停止施工,不因可选的恢复、平台扩展或强化隔离工作延迟交付。 ## 11. 风险与停止条件 | 风险 | 处理边界 | | --- | --- | | listener FD 无法穿过某种初始化程序 | 先报告不兼容点;不静默退回暴露原始 socket,也不改成双层 PTY。 | | UID 或 namespace 组合改变 RMUX 的连接身份行为 | 对拟支持的组合做窄范围验证;当前原型通过不能推导所有组合都受支持。 | | 撤回后仍有订阅输出或新请求获准执行 | 这是 MVP 阻塞问题,必须在受限入口执行层修复。已交付的字节和已执行的 shell 副作用不在撤回范围内。 | | 自动清理与单终端访问权冲突 | 按 M0 的操作授权表拆清非破坏访问与管理操作,不另建任务实体。 | | 弱沙盒能访问内部控制面 | 修正示例部署或明确该环境的旁路能力;不把弱隔离宣称为完整安全隔离。 | | 改动开始需要新的服务、持久状态体系或通用抽象 | 停止扩展,回到明确验收项判断是否必要;未经确认不加入 MVP。 | 本计划记录后续工作的范围与顺序,不表示当前已开始实现,也不构成 Git 提交、推送或修改参考项目的授权。