Files
NotesAgentic/docs/architecture/第三阶段生产化工程规划与验收目标.md

273 lines
39 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.
# 第三阶段五项生产化能力:工程规划与验收目标
日期:2026-09-08。审计基线:`2010778780fa7267557a7302132e63ac533d43ab`,分支 `feat/phase3-completion`。初始规划提交:`2c4287e`。用户随后授权全量实施,并确定正式名称为 **OpenNexus**。状态:**实施中,五项均未通过完整生产验收**;实际证据见[实施记录](../development/OpenNexus生产化实施进度-2026-09-08.md)。
## 1. 范围、文档优先级与交付口径
本文细化[第三阶段实施规划](第三阶段实施规划.md)中的 D05D07、C02/C03、S02–S06 及关联发布门禁。上述五项的生产目标、依赖、迁移和量化验收以本文为准;总规划继续约束第三阶段其他范围。[Host v1](../contracts/Host-v1契约.md)、[Sync v1](../contracts/Sync-v1契约.md)、[Community v1](../contracts/Community-v1契约.md)继续描述当前接口,不因本文自动变成已实现的生产契约;实施 PR 必须同步相应契约。[实施与验收记录](../development/第三阶段实施与验收记录.md)只记录实际执行证据。
“通过”要求实现、自动化报告、平台实测和恢复演练同时满足。旧记录中的测试数量不继承为本计划证据。五项完成不代表多窗口、全部社区内容能力、OCR/音频质量等第三阶段其他退出项完成。
初始规划轮仅允许文档提交;后续实施已获用户授权。实施期间测试只使用自动生成的临时 Vault、测试密钥和隔离服务,不读取个人笔记、凭据或生产数据库。
## 2. 现状审计与差距
下表路径相对仓库根;均已核对代码或配置,不仅依据旧规划。
| 能力 | 代码证据与现状 | 必须补齐的差距 |
| --- | --- | --- |
| AI Core Sidecar | `frontend/src-tauri/src/main.rs``core_url` 固定 `127.0.0.1:8000``backend/app/main.py` 配置 CORS`tauri.conf.json` 的 bundle 关闭且 CSP 允许直连该端口;`frontend/package.json` 的 desktop:build 带 `--no-bundle` | 自动打包/启动、受控握手、全端点认证、流式与二进制转发、动态地址、退出监管;CORS 不能充当认证,开发 EXE 不能充当安装包 |
| Stronghold | `backend/app/providers/credentials.py` 的 Fernet 使用 `credentials/master.key` 或环境主密钥及 `credentials.json`;有 Provider/Plugin/MCP 命名空间限制与环境 resolverCargo 无 Stronghold 依赖 | Rust 唯一凭据所有者、解锁/锁定、受限解析代理、可恢复迁移、清除确认、密码丢失处理;禁止直接复制环境 resolver 到生产 |
| OS 沙箱 | `backend/app/extensions/mcp.py` 仍由 Python Popen 启动;`runtime.py``mcp_registry.py` 对开发外不受控启动设禁用门禁 | Rust 进程启动与平台策略、句柄级文件约束、网络代理、资源限额、逃逸测试;进程树清理或 capability 本身不证明隔离 |
| Extension Manager | `backend/app/extensions/installed.py` 已有 SQLite 登记、目录摘要、受管理 ZIP 根、重启复核;Community v1 已有签名及分类限额;Rust capability 的 extensions=false | Rust 单一安装/授权库、迁移与接管、跨包事务升级、许可绑定、撤销和回滚;不得重复宣称安装登记尚无实现 |
| 同步客户端 | `frontend/src-tauri/src/workspace.rs` 已有 files/journal/file_ops/outboxremote 写入不回流;outbox 保存正文且仅 pendingHost sync=false | 上传/拉取状态机、服务器基线映射、分块附件、确认丢失恢复、冲突 UI、配置分类、监听与初次绑定;本地 revision 不等于服务器 revision |
| 同步生产服务 | `server sync/sync_server/app.py` 已有认证、设备撤销、CAS、幂等、上传和历史;PostgreSQL 行锁分支与 S3 adapter 已存在;SQLite Fixture 测试;`/ready` 只探测数据库;Compose/README 有手动 Bucket 初始化 | PostgreSQL+MinIO 真链路、跨 worker 上传一致性、过期暂存清理、流式 IO、部署初始化、备份恢复、指标和压测;禁止把 Compose 文件存在等同运行通过 |
附加审计:`storage.py` 的 S3 get 返回整块 bytes,上传 complete 读取整个暂存文件;100 MiB 并发场景需测峰值内存并改造流式路径。现有目录树摘要包含相对路径及内容,不能与 Community 的 ZIP SHA-256 混用。当前 `.gitea/workflows/ci.yml` 有文档、后端、服务 Fixture、前端和 Rust lib 检查,尚无五项生产验收作业。
## 3. 目标架构与责任
```mermaid
flowchart TD
UI[本地 WebView] --> IPC[Rust 窄 IPC 与能力校验]
IPC --> WS[Workspace 唯一写入者]
IPC --> CM[Core Supervisor]
CM --> CORE[受信任 AI Core Sidecar]
CORE --> BROKER[Host 凭据与工具代理]
BROKER --> SEC[Credential Broker / Stronghold]
BROKER --> EM[Rust Extension Manager]
EM --> SB[OS 沙箱与受限 IO 代理]
SB --> EXT[不可信 Plugin / MCP]
WS --> Q[Sync outbox / inbox / 基线映射]
Q --> SYNC[HTTPS Sync Server]
SYNC --> PG[PostgreSQL]
SYNC --> S3[私有 S3 对象存储]
```
| 组件 / 建议模块(待新增) | 唯一职责及禁止越界 |
| --- | --- |
| `src-tauri/src/core/` | 验证 Sidecar 清单、版本、进程身份和就绪;转发显式路由与事件;不向 WebView 返回会话密钥 |
| `src-tauri/src/credentials/` | Stronghold 会话、迁移日志、作用域 resolver;前端可提交新秘密但只收到 ID/是否配置/锁定状态,永无读取明文命令 |
| `src-tauri/src/sandbox/` | 唯一第三方进程启动器,执行平台策略;不能由 AI Core 或前端要求任意 shell |
| `src-tauri/src/extensions/` | 包、依赖、授权与升级事务;AI Core 只消费当前已校验工具描述和受控调用句柄 |
| `src-tauri/src/sync/` | 会话轮换、outbox/inbox、附件流、冲突记录、远端基线;一律经 Workspace journal 落盘 |
| 现有 Workspace | Vault 授权、稳定 file_id、expected_hash 校验、日志与原子替换;补附件/外部变化能力,不能让 Sync 自行写文件 |
| `server sync/` | 独立 Sync API、元数据/对象一致性、备份恢复与配额;不运行 AI/插件,不复用社区登录 |
| 前端 platform adapter | 显示真实能力、进度、锁定、冲突与恢复选择;Web 模式维持独立开发后端,不与桌面共同写同一 Vault |
Rust Host IPC 增补 DTOrequest_id、vault_id、session_generation、operation_id、expected_hash、超时与取消;字段按操作适用,不接受前端指定设备身份。统一错误至少区分 AUTH_REQUIRED、CORE_UNAVAILABLE、PROTOCOL_INCOMPATIBLE、CREDENTIALS_LOCKED、MIGRATION_CONFLICT、SANDBOX_UNAVAILABLE、PERMISSION_CHANGED、REVISION_CONFLICT、QUOTA_EXCEEDED。取消只停止未提交步骤,已提交结果须返回 committed 状态或可查询 operation_id。
## 4. 信任边界与威胁模型
| 边界 / 攻击者 | 主要威胁 | 强制控制与验证 |
| --- | --- | --- |
| WebView、远程页面、不可信笔记内容 → Host | XSS 调用凭据/文件/shell、跨 Vault、路径穿越 | 仅本地授权窗口 capability;命令白名单、DTO 限额、绑定当前 Vault;远程来源、伪造 vault_id、编码路径负向测试全部拒绝 |
| 同机普通进程 → Core | 抢占固定端口、伪就绪、重放令牌、从 argv/日志拿令牌 | 随机端口+受控继承管道的双向随机挑战;会话代际、全路由鉴权;旧代际不得访问新实例 |
| AI Core → Host/凭据 | 过宽 resolver、借工具参数跨命名空间、直接写 Vault | Core 属于受信任计算基;按 Provider/工具身份与 Vault 授权解析,笔记写入经 Host CAS;后端桌面模式禁止直接写入 |
| 恶意扩展及其后代 → OS | 读取家庭目录/保险库、联网泄露、派生 shell、资源耗尽、符号链接竞态 | OS 默认拒绝;文件/网络通过 broker;句柄固定、参数数组、环境白名单、进程树限额;不能以用户勾选跳过隔离 |
| 社区/下载/CDN → 安装库 | 包篡改、签名键被撤销、ZIP 穿越、依赖替换、旧版本回放 | 固定信任根、不可变发行、每次安装在线复核撤回状态、签名及 ZIP/目录双摘要、事务 staging |
| 其他 Sync 用户/被撤销设备 → 数据 | 猜摘要读对象、跨 Vault、复用上传、重复提交、重放 Refresh | 每个 API/对象鉴权,绑定 owner+device+vault,数据库 CAS/幂等;跨 worker 测试 |
| 崩溃/断电/磁盘满/服务故障 | 半写、游标先行、数据复活、备份缺对象 | 先持久化意图后提交;远端确认可重放;游标只随已落盘 inbox 提交;一致切点备份 |
不承诺抵御管理员/root、已被控制的 Host/内核、内存取证或运行中同用户调试器。Stronghold 保护静态存储,不能使运行时明文永不存在。Sync 服务运营者可接触明文;TLS 不等于 E2EE。扩展不能获得 Stronghold 文件或 Core 管道,即便获准执行某个工具。
## 5. 平台与容量基线
以下是待落实的首发验证范围,不是兼容性声明。P0 为 Windows 11 x64、NTFS 本地 Vault、MSVC 发布构建、标准用户安装与运行;现有 GNU 开发构建保留回归但不替代发布证据。Windows ARM64、UNC/网络盘不在首发支持范围。macOS 14+ arm64、Ubuntu 24.04 x64 为后续平台门;未通过只能标预览/禁用相应能力,总阶段三平台门仍保留。
沙箱候选:Windows AppContainer/受限 token + Job Object + brokerLinux namespaces/Landlock/seccomp/cgroup + brokermacOS 独立签名沙箱 helper/XPC + broker。这里只冻结验证方向,**不宣称这些组合已经可用或覆盖全部系统版本**。P0 原型必须固定实际 API、OS build、文件/网络规则与最低支持版本;原型失败则该平台第三方执行保持禁用,并阻塞该平台“五项完成”声明。禁止退回普通子进程或依赖废弃工具绕过门禁。
基准机器:客户端 4 核/16 GiB/SSD;服务 4 vCPU/8 GiB/SSD,独立 PostgreSQL 与 MinIO100 Mbps、RTT 20 ms,故障网络附加 5% 丢包。数据:固定 seed=2026090810000 篇每篇 4 KiB 笔记、中文/NFC/大小写路径向量、一个 100 MiB 二进制附件、两个账号每账号两个 Vault。报告记录 CPU/OS build/磁盘/依赖 lock 摘要及网络实测。下文阈值为工程发布目标,调整必须有基准报告与文档评审,不能测试失败后直接放宽。
## 6. 依赖、工作包与交付物
角色是责任域而非已确认个人排期:H=Rust/桌面负责人,B=AI Core负责人,E=扩展安全负责人,S=Sync服务负责人,F=前端负责人,R=发布/运维负责人。M0 必须落实到人,安全评审与发布密钥管理不得由未指定角色代签。工作包完成须含实现、契约变更、自动化用例和操作证据。
依赖顺序:P0 → A/B 原型 → A+B → C → D 生产启用;D 的纯安装事务可在 C 前开发,但不能启动第三方代码。S1/S2 可在 P0 后独立推进;S3/S4 依赖 B 的令牌存储及 Workspace 附件/监听/恢复改造;最终 E2E 依赖 A–D、S 全部退出。前端用显式 Fixture 开发,发布跑真实 Host。这里的工作包安排不授权当前文档任务开始实现。
| ID / 负责人 | 工作包及必须交付物 | 依赖 / 退出证据 |
| --- | --- | --- |
| P0 / H+E+S+R | Host认证/Stronghold/沙箱 ADRDTO、错误、schema迁移方案,平台原型,冻结测试 runner 与环境清单 | 审计基线;每个待决项有选择和负责人,测试清单无空 ID |
| A1 / H+B | 锁定 Python 与依赖,按平台生成独立运行目录(优先 PyInstaller onedir 原型),资源清单/SBOM/许可证、固定 runtime 摘要;模型与 CUDA 独立下载 | P0;干净机无 Python 启动,包内容复核 |
| A2 / H+B | Supervisor、握手/鉴权中间件、Host 路由与 SSE/二进制桥、动态 CSP/资源方案、桌面写入代理 | A1A-01~03 |
| A3 / H+R | 安装/更新签名与版本清单、进程故障恢复、升级回滚 runbook | A2;A-04、E-04;新发布命令不能沿用带 no-bundle 的命令假装出包 |
| B1 / H+B | Stronghold Broker、解锁/锁定/更改密码,Provider/Plugin/MCP/Sync分域适配 | P0B-01、03 |
| B2 / H+B | Fernet迁移器、schema/引用映射、断点日志、备份与清除 UI | B1;B-02、04 |
| C1 / E+H | 各平台最小 OS 沙箱与攻击夹具、不可用能力报告 | P0;C-01 及平台证据 |
| C2 / E+B | 文件/网络/工具 broker、许可绑定、资源与进程生命周期、移除生产 Python直启 | A2+B1+C1C-0204 |
| D1 / H+E | Rust安装库、签名向量互验、旧记录只读迁移、Python停止写入适配 | B2D-01、02 |
| D2 / H+F | 安装/依赖锁/权限差异/升级/回滚/卸载/撤销完整事务和UI | D1+C2D-03、04 |
| S1 / S | PostgreSQL/MinIO集成、跨worker上传锁与磁盘持久化、对象流式IO、过期暂存清理 | P0;S-04、05 |
| S2 / S+R | 部署初始化、TLS/就绪/限流/指标、升级、备份/恢复命令与runbook | S1S-06、07、09 |
| S3 / H+F | Rust outbox/inbox、远端基线、账号绑定/刷新、附件、监听、分类过滤、同步状态 | B1+S1+Workspace改造;S-01、02、08 |
| S4 / H+F+S | 首次对账、离线冲突/删除恢复/历史UI、两台实机及故障重放 | S2+S3S-03、E-0103 |
| R0 / R+全组 | 有签名安装产物、全套报告索引、平台支持声明与恢复演练 | 所有适用验收ID通过;发布门禁表归档 |
## 7. Sidecar 打包、认证与生命周期设计
生产 UI 仅经 Host IPC 获取业务与事件,移除固定 8000 直连(含图片/导出 URL),对大对象采用受控流/临时资源句柄,句柄限定窗口、Vault、有效期。Core绑定 `127.0.0.1:0`;禁止先查空闲端口再绑定的竞态。Host通过专用继承管道下发256位随机会话秘密并验证基于该秘密的挑战应答;子进程回报实际绑定地址、PID、协议及构建摘要。通道只对目标进程继承,其他子进程不得继承。秘密不放argv、环境、URL、磁盘或WebView。
FastAPI在业务处理和日志之前验证会话认证,覆盖health、API、SSE、文件下载、错误路径及文档端点;生产关闭OpenAPI/docs或同样鉴权。校验Host/Origin且拒绝重定向;无Origin的非浏览器请求仍必须认证。每次重启换代并销毁旧管道。Host代理由当前宽泛 `/api/*` 收敛成版本化路由/方法/大小表;拒绝任意主机、绝对路径、双重编码与CRLF。
状态:stopped → starting → ready → degraded/backoff → stopped;就绪超时30秒,连续失败退避1/2/4/8/16秒,5分钟内最多5次自动重启,超过后停在可重试错误。编辑不等待Core恢复;推理任务标 interrupted,不自动重放有副作用工具。正常退出先取消任务,5秒宽限,10秒内清理整个受管理进程树。预下载模型缺失显示模型不可用,不下载基础启动必需代码。
Host/Core主协议不兼容时不转发业务写操作,但本地Rust编辑可用。更新以Host+Core兼容组合原子切换;签名或摘要失败保留原版本。基础安装大小目标≤300 MiB(不含模型/CUDA);若A1原型无法满足,必须在发布前明确拆分方案与新预算。
## 8. 凭据迁移与恢复
生产凭据按`provider``plugin/<id>``mcp/<id>``sync/<server>/<account>`分域。保留既有外部credential_id映射;调用者身份由Host确定,不能信任请求自报命名空间。Core只在调用时获准解析所需Provider秘密;远程MCP认证优先由网络broker添加。确需环境秘密的本地MCP必须显式声明,只注入该沙箱进程,不能继承全量环境。新秘密输入与迁移过程不进入诊断、前端持久化或普通同步。
默认使用用户口令解锁Stronghold;系统安全存储自动解锁作为后续独立选项,不能把解锁密钥写同目录文件。锁屏、手动锁定和应用退出锁定Broker,取消未开始的凭据调用并清除缓存;已发送给Provider的远程请求无法收回。禁止前端加载可读取明文的通用Stronghold能力。密码遗失且无可解锁备份时只能重新配置凭据,笔记继续可用,不提供假恢复或回退明文。
迁移状态:discovered → backed_up → copied → verified → switched → cleanup_confirmed。迁移锁阻止旧新库同时修改;备份Fernet密文/主密钥时保持原访问限制,备份不进入Vault。环境主密钥仅从当前进程受控读取,缺失时停止,不尝试新建替代钥匙。逐条解密写入Stronghold并重新打开验证;日志只记录源ID、目标ID、状态及密文文件校验,不记录秘密或秘密哈希。
新库同ID异值进入冲突,不覆盖;用户选择保留新值/重新命名旧引用后再切换。全部验证完成才原子更新resolver世代和引用映射。任一步失败保留旧数据且重试幂等;切换后不得静默fallback Fernet。只有验证成功并由用户明确确认清除旧存储才删除旧密文/受管理master.key与本次备份;外部环境主密钥不由应用删除。清除不承诺SSD物理擦除。清除前可用旧版本恢复旧快照;切换后新写入不自动回灌旧库,降级需保留新库并阻止旧版本写入或重新配置凭据。
## 9. OS 沙箱与扩展事务设计
沙箱默认无Vault目录、家庭目录、保险库、原始网络及任意后代执行权限;只挂载校验过的只读包、独立可写scratch。笔记读写经文件broker按当前授权和expected_hash执行;路径以已打开目录句柄约束,覆盖junction/symlink/hardlink与校验后替换。网络broker按HTTPS目标、端口、DNS解析和每次重定向复核;默认拒绝loopback、私网、link-local和云元数据目标。DNS重绑定不能绕过。配置为自托管私网MCP需单独显式目标授权,不能开放整个网段。
许可摘要绑定source、package_id、version、ZIP与展开摘要、入口、参数数组、环境声明、权限集、Vault、平台策略版本及到期时间;任一改变使许可失效。禁止shell拼接。每实例默认512 MiB、16个受管理进程、scratch 256 MiB、单工具60秒;CPU预算固定为单核等效,持续超限10秒终止。无法执行所需限额的平台不得返回“已隔离”。broker本身做请求大小/速率限制,避免扩展绕过进程限额耗尽Host。
Rust安装库置于应用数据目录,记录schema、来源、签名键、两种摘要、依赖锁、配置schema、启用意图、许可和事务日志。旧`extension-installations.sqlite3`只读导入;外部目录标本地未信任,不移动/删除原目录。用户同意接管的包复制到受管理版本目录后复核,旧许可全部失效。迁移成功设置所有权标记,桌面Python注册API改为代理或拒绝;Web开发模式保留独立数据目录,不共用安装库。
事务:下载暂存 → 签名/撤回/ZIP校验 → 安全解包 → schema/依赖锁 → 确认权限 → 停旧实例 → 备份配置 → 切换活动指针与数据库日志 → 健康检查 → 完成。依赖按拓扑排序;环、缺失、版本冲突在停旧实例前失败。配置迁移在副本上执行,运行迁移代码也必须沙箱化。失败恢复旧包+旧配置,但不恢复已撤销签名或失效许可;这种情况保留旧数据并禁用运行。
下载时沿用Community v1分类限额,禁止将Theme和Plugin限额合并。离线允许浏览缓存、运行已校验且许可未过期的包;新的社区安装因无法复核撤回状态阻止;本地包走独立信任确认。每次联网启动/启用复核撤销,成功收到撤销后5秒内停止相应运行实例。卸载先阻止新任务/检查依赖,再停进程、注销工具并清理受管理版本;外部目录及用户配置默认保留,删除秘密单独确认。
## 10. 完整同步及生产运维设计
### 10.1 客户端数据与状态机
保留Sync v1、file_id、operation_id及当前CAS语义,不以路径派生身份。新增remote_heads保存server/vault/file的服务器revision;本地files.revision只表达本地编辑次数。outbox新增server_base_revision、payload引用、attempt、next_retry和状态;同文件后续本地操作需等前序远端确认再确定CAS基线,不能简单复制本地revision。大附件转入不可变本地spool,通过摘要引用,不把100 MiB放入SQLite正文列。迁移旧pending时先对账,未知远端基线进入冲突/首次绑定,禁止猜测base_revision。
上传状态:pending → uploading → uploaded → committing → acked,另有retry/conflict/blocked_auth/blocked_quota。请求响应丢失复用同一operation_id;同ID不得改变payload。分块最大1 MiB,对象最大100 MiB;续传先查询offset。刷新采用会话互斥,轮换成功原子存入Stronghold;刷新响应丢失无法恢复时要求重新登录,不无限重试旧Refresh或丢弃outbox。
拉取:冻结分页boundary → 验证对象长度/摘要 → 持久化inbox → Workspace日志落盘 → 同事务推进已应用cursor和remote_heads。崩溃重放以revision/operation_id去重。存在本地pending的同file_id不能覆盖,保存双方和冲突记录;用户选择本地/远端/另存均形成可追踪新操作。rename/delete复用身份,远端删除遇本地编辑产生冲突,不自动复活。历史恢复创建新revision而非修改历史。
常态主动轮询5秒,错误指数退避1~60秒加抖动,尊重Retry-After;通知未来只作拉取提示。显示本地已保存、待同步数量/字节、最近成功时间、认证/配额阻塞与冲突计数,不能用一个“保存成功”代替同步状态。暂停/解绑保留本地文件,明确封存旧绑定outbox,重新绑定必须对账,不向新Vault重放旧队列。
初次绑定先生成新增/冲突/删除预览;空本地库仅拉取,不把缺失解释为删除。首次启用自动导入现存文件时保存稳定ID映射并防止重复登记。补监听去重和外部重命名对账:无法可靠匹配时展示删除/新增候选,不猜身份。旧Host schema先备份并以事务迁移,失败保留源库;旧客户端遇新schema拒绝写入。
### 10.2 数据分类与兼容
默认同步Markdown、用户附件、任务、用户Skill/配置、主题配置;任务/配置使用版本化逻辑记录和字段白名单,不能复制SQLite数据库。实现PR需在Sync契约定义这些记录的编码/命名空间及迁移版本,兼容v1文件传输时也不能混入可执行包。对话、Agent历史、人设、布局、一般Provider参数用户选择后同步;运行中任务不跨设备自动恢复。安装清单可选,仅同步ID/来源/版本/摘要,目标设备重下载、重校验、重授权。API Key、令牌、设备许可、环境变量、日志、索引、向量、模型与缓存禁止普通同步。未知配置字段默认排除,需schema升级后放行。
### 10.3 服务事务与恢复
继续PostgreSQL+私有S3,历史revision/tombstone/已引用对象永久保留,不实施历史GC。过期上传暂存可清理,但须锁定状态、避免与complete竞争;对象写成功但DB提交失败产生的未引用对象先隔离审计,不自动删历史。配额计算包含历史和上传预留,满额拒绝新增,不影响读取/导出。
首发支持单服务节点内2个worker,共享持久staging卷;跨主机水平扩容为非目标。上传offset与磁盘刷盘要定义崩溃恢复:磁盘超过已确认offset截断至确认点,磁盘不足则将上传标损坏并要求重传;不得报告未持久化offset。DB锁串行化同上传写入,complete幂等且对象先于可见revision。读取与上传流式处理,服务以限流/背压保护内存。
S2交付可重复初始化命令,创建私有Bucket和最小权限服务账号、交互创建用户;Secrets由部署环境注入,不写仓库/命令参数。Compose镜像锁定经验证digest并扫描,当前tag存在性与可拉取性在部署门验证。TLS入口与8080 loopback隔离;/health仅存活,/ready探测schema、staging读写和测试前缀S3读写删除,失败返回503。探针不触碰用户对象,超时≤3秒,缓存≤5秒。
备份先暂停写入并排空在途提交,取得DB与Bucket一致切点及对象清单,包含schema、服务版本、游标高水位、摘要与加密备份位置;备份完成才解锁写入。恢复到全新隔离实例,逐个验证所有被历史引用对象,再切换入口。每日备份目标RPO≤24小时、基准1 GiB数据集RTO≤30分钟;事故期间未备份数据的风险必须展示,不把RPO说成零丢失。上传暂存可不恢复,客户端重新申请上传;已提交历史不得缺对象。部署升级先备份,迁移失败不启动新写入;不可逆schema禁止旧服务启动,恢复必须DB+对象同一切点,不只回退镜像。
## 11. 自动化验收接口与测试矩阵
### 11.1 现有命令与拟新增命令的区分
当前文档改动使用仓库根的`python scripts/check-doc-links.py``git diff --check`。实现阶段仍须运行现有检查:
```powershell
# 以下工作目录均相对本工作树根;不是本次已运行的功能检查
uv run --directory backend pytest
uv run --directory 'server sync' pytest
uv run --directory community-server pytest
pnpm --dir frontend test
pnpm --dir frontend type-check
pnpm --dir frontend build
cargo fmt --manifest-path frontend/src-tauri/Cargo.toml --check
cargo test --manifest-path frontend/src-tauri/Cargo.toml --lib --locked
cargo clippy --manifest-path frontend/src-tauri/Cargo.toml --lib --locked -- -D warnings
```
验收入口已经建立:仓库根执行`python scripts/phase3-production-acceptance.py --suite <suite> --config <isolated-config> --report-dir <output>`suite取`sidecar/credentials/sandbox/extensions/sync-client/sync-service/e2e/all`,支持`--case <ID>`单例复跑。配置由[隔离模板](../../scripts/phase3-acceptance-config.example.json)产生,含临时数据根、服务URL、安装包路径、平台profile、seed及秘密的环境引用,不能含生产凭据。runner拒绝仓库个人Vault、未标记隔离环境、非空报告目录和未提交的非 Vault 源码;缺case driver、依赖/安装包/权限或必测项skip均以非零退出,不能只打印成功。生产 driver 正逐项接入,未登记项明确报告 `NOT_IMPLEMENTED`,不因 runner 接口存在而视为已验收;使用和结果契约见[生产验收 Runner](../development/OpenNexus生产验收Runner.md)。
runner输出JUnit、逐ID JSON、耗时/峰值内存、进程树/拒绝访问计数、文件摘要与revision清单及脱敏日志。退出0仅表示该suite所有适用ID通过;`all`要求显式平台profile并检查缺失项。下表同一行内所有断言均必测,每类输入保留独立子用例;测试代码硬编码预期协议向量或独立oracle,不能用被测函数自证。P0需生成ID清单并校验报告完整性。
| ID / suite | 操作与可自动验证标准 |
| --- | --- |
| A-01 / sidecar | 干净标准用户VM无Python且禁网,安装已签名包并连续启动20次;Core ready P95≤10秒(无模型加载),20/20可编辑保存;基础安装包≤300 MiB;篡改Core一字节20/20拒启 |
| A-02 / sidecar | 覆盖全部可用路由含SSE/二进制及受保护docs;缺失/错令牌/旧代际各100次均401或403且零业务副作用;若关闭docs则始终404且不暴露schema;伪握手/抢占8000不被接受;前端存储、日志、argv、环境扫描测试秘密命中0 |
| A-03 / sidecar | 模拟不兼容协议返回PROTOCOL_INCOMPATIBLE且业务写入064 MiB边界/超界、流取消、断流均符合冻结DTO限额;未提交取消无副作用,已提交结果可按operation_id确认 |
| A-04 / sidecar | 连续杀Core6次验证退避和熔断;每次本地保存原文摘要不变;关闭Host10秒内受管理子孙进程为0;更新中断每个切换点重启20次均恢复完整兼容组合 |
| B-01 / credentials | Provider/Plugin/MCP/Sync每域3个测试秘密;同域调用成功,所有跨域组合被拒;WebView只获引用/状态,无通用明文读取;日志/诊断/同步payload命中秘密0 |
| B-02 / credentials | 100条Fernet夹具,含环境主密钥、空库、坏token、重复ID异值、缺钥匙;成功迁移计数/ID/解密值逐条相等;重复迁移3次不增加记录;坏项/冲突使切换停止且旧文件摘要不变 |
| B-03 / credentials | 锁屏/手动锁定后1秒内新解析返回CREDENTIALS_LOCKED;错误密码不得解锁;改密后旧密码失败新密码成功;损坏库可恢复测试备份,无法恢复时编辑仍通过 |
| B-04 / credentials | 每个迁移持久化边界kill进程20次;恢复只处于完整旧/新状态,无丢失/重复;未确认清除时旧文件始终存在;确认后受管理旧文件不存在,外部环境钥匙未修改;旧版本不得写新schema |
| C-01 / sandbox | 各声称支持的平台运行真实恶意二进制,尝试读写未授权Vault/家庭/保险库/其他包、loopback/公网/私网/元数据/原始socket;每类100次OS或broker拒绝,诱饵文件摘要不变、禁止网络收包0;策略不可用返回SANDBOX_UNAVAILABLE且进程数0 |
| C-02 / sandbox | 参数shell元字符、环境注入、子进程逃逸、链接竞态、DNS重绑定、重定向各100轮零越权;授权文件读/工具调用与授权HTTPS各20次成功,证明并非全部禁用来过测试 |
| C-03 / sandbox | 逐个改入口/参数/权限/摘要/Vault/策略版本/到期时间均PERMISSION_CHANGED或许可过期拒绝;从Python旧启动API绕过亦拒绝;已撤销许可重启不恢复 |
| C-04 / sandbox | 分别耗尽512 MiB内存、256 MiB scratch、16进程、CPU及60秒工具预算;达到上限后≤10秒清理子树,Host不退出且本地保存成功;broker超限请求返回明确限流错误 |
| D-01 / extensions | Python生成的Community真实签名向量在Rust验证;签名/ZIP/权限/键撤销任一篡改拒绝;Theme和其他包各边界/超界、穿越/链接/炸弹/重名全部阻止,安装根外新增文件0 |
| D-02 / extensions | 受管理、外部、被修改、缺失旧包各一组;只读迁移重复3次记录不重复;外部目录摘要不变;摘要不同不能继承信任;迁移后旧Python写API拒绝且原许可无法启动 |
| D-03 / extensions | 每个事务边界注入断电/磁盘满/配置迁移错误各20轮;active包与配置永为匹配旧版或新版,无混合;依赖环/缺包/冲突在停止旧实例前拒绝;失败不扩大授权 |
| D-04 / extensions | note-reviewer与markdown-workbench真实安装→启用→调用→升级→回滚→撤回→卸载;收到撤销5秒内进程与注册工具均0;卸载重启不复活,外部路径不删;离线新社区安装拒绝 |
| S-01 / sync-client | 两客户端同文件离线连续编辑20次再连;逐操作remote base链有效,最终正文/hash/file_id一致;响应丢失后重复提交100次只产生1条对应revision,无pending静默消失 |
| S-02 / sync-client | 100 MiB附件每10 MiB断网并杀进程,查询offset续传;最终长度/hash相等;pull各持久化边界kill20轮,cursor绝不超过已应用revisionremote落盘不新增outbox |
| S-03 / sync-client | 同改、改对删、改对rename、同目标rename、历史恢复各20次:冲突保留两端可恢复内容;解决后双方收敛;空Vault首次绑定产生服务器delete数0;解绑再绑不重放旧队列 |
| S-04 / sync-service | 真PostgreSQL+MinIO、2 worker100并发同base不同操作,仅1个成功其余409;相同幂等键同payload重试返回同结果;不同payload拒绝;分页无漏无重;对象先于可见revision |
| S-05 / sync-service | 两worker并发同upload offset、磁盘超前/落后、complete响应丢失、过期清理竞争各100轮;offset不超过持久字节,重复complete不重复计费,无损坏已引用对象;过期暂存在到期后15分钟内清除并释放预留 |
| S-06 / sync-service | 跨账号/Vault猜hash、被撤销设备、过期token及上传链接各100请求全拒绝;撤销提交后下一个请求即拒绝;数据库/S3/staging故障后8秒内ready=503,恢复后8秒内ready=200;限流返回429和Retry-After |
| S-07 / sync-service | 自动部署至空实例、重复初始化无数据覆盖;备份1 GiB/10000文件后删测试实例再恢复,所有历史引用对象hash校验100%,RTO≤30分钟且最近备份年龄≤24小时;迁移失败旧数据摘要不变 |
| S-08 / sync-client | 分类夹具覆盖默认/可选/禁传/未知字段;默认集合全量往返;未选择的可选数据上传0;秘密/索引/缓存上传0;目标设备安装清单不继承许可;schema不兼容拒写且队列保留 |
| S-09 / sync-service | 基准环境10客户端连续30分钟、每秒总20次4 KiB提交,API P95≤500 ms、意外5xx<0.1%、数据校验100%4并发100 MiB上传服务总RSS≤2 GiB10000笔记+附件初次同步≤10分钟,无故障增量收敛P95≤10秒 |
### 11.2 分层测试矩阵
| 层级 | 环境与覆盖 | 执行门 |
| --- | --- | --- |
| 单元/契约 | Rust状态机、Python认证、前端错误/取消;旧新schema、签名跨语言向量、Sync协议字段 | 每个实现PR,不能只执行Rust默认lib遗漏desktop功能 |
| 集成 | 实际Host+打包Core+Stronghold+沙箱helperPostgreSQL/MinIO双worker,隔离Vault | 对应工作包合入前,A–S全部适用ID |
| 故障与安全 | 网络丢包/重排/重复、磁盘满/权限拒绝、进程kill、恶意ZIP/二进制、迁移中断 | 每个RC;只在可销毁VM/容器中执行 |
| 安装实机 | Windows标准用户无Python/无工具链;macOS/Linux各自构建和隔离证据;离线/升级/卸载 | 每个声称支持的平台,安装包签名验证并实跑 |
| 性能与运维 | 固定基准、20次冷启动、30分钟负载、一致备份恢复 | 每个RC;原始指标+分位数报告,不能只给平均值 |
| UI端到端 | 主窗口/锁定提示/凭据迁移确认/扩展权限差异/冲突选择/同步状态;键盘、中文、100/150/200%缩放 | 两台独立设备实际操作,自动断言+必要截图 |
## 12. 端到端验收场景
通过统一runner的`--suite e2e --case E-xx`执行,实机操作者只完成安装、口令输入及明确用户确认;动作记录与机器断言必须归档,不能用mock替代。
| ID | 场景与结果 |
| --- | --- |
| E-01 | 两台干净Windows设备安装同一签名RC;A迁移测试Fernet,B使用独立凭据;A离线编辑再联网同步,B拉取;两端file_id/hash/remote revision一致,B从未收到A的凭据,禁网时本地编辑持续成功 |
| E-02 | 两机断网同改一笔记且A删除另一笔记/B修改它;重连展示2个冲突记录及各自内容;分别选择另存/保留本地,最终清单一致、待同步=0、无未确认删除与静默丢失;恢复历史增加revision |
| E-03 | 安装两个真实社区样例,授权后执行经Host写笔记并同步;恶意MCP读保险库和访问未授权网络均被拒;升级引入权限变化保持禁用,重新授权后运行;撤回停止实例,卸载不删用户笔记 |
| E-04 | Core崩溃、Stronghold锁定、上传途中断网、安装升级中断依次注入;保存摘要始终可恢复;重启后迁移/安装/同步日志收敛,无孤儿进程;恢复旧RC时拒写不兼容schema并能按runbook恢复匹配快照 |
| E-05 | 停服务并从一致备份恢复到新实例,保留服务器身份配置;两端重新握手后对账;已备份历史hash全等,未备份本地pending仍可上传;撤销设备后该设备下次请求拒绝、UI显示重新授权需求且本地内容保留 |
## 13. 可观测性与发布门禁
所有组件结构化事件包含request_id/operation_id、匿名化vault/device标识、组件版本、状态转移、错误code、耗时/重试计数;跨Core/Host/Sync可关联。默认不记录正文、绝对个人路径、Authorization、秘密、完整环境/命令参数。测试种植秘密扫描所有日志、崩溃附件和诊断包必须0命中;诊断包由用户主动导出,默认不含数据库/保险库。日志轮转每文件10 MiB、最多5份;服务默认保留14天,详细审计期限由部署者配置并展示。
指标至少包括Core就绪/重启、保险库锁定/迁移失败数、沙箱拒绝/资源杀死、安装事务阶段/回滚、outbox条数/字节/最老年龄、pull延迟/冲突、API延迟/错误、对象摘要失败、过期暂存、配额、最近可恢复备份时间。禁止使用原始路径/secret ID做指标label。在线且无认证/配额/冲突阻塞时队列最老年龄>60秒持续2分钟告警;备份年龄>24小时、任意摘要不匹配或迁移失败立即告警;意外5xx>1%持续5分钟告警。
| 门禁 | 必须提供的证据 / 拒绝条件 |
| --- | --- |
| G0 设计 | P0 ADR、责任人、平台实测原型、字段与错误冻结、ID清单;未确定隔离策略不能进入生产启用 |
| G1 实现 | 现有CI及desktop编译/测试、五项runner适用ID全部通过;必测skip、Fixture代真服务、缺失报告均阻塞 |
| G2 安全与迁移 | A/B/C/D负向测试、上版数据升级和断点恢复;任何越权、秘密泄露、数据丢失、失效许可复活均阻塞,不可凭用户点击豁免 |
| G3 运维与性能 | S-0409、E-05、干净部署/备份恢复runbook由另一操作者复跑;容量/时延超阈值须修复或事先评审修改范围 |
| G4 发布产物 | 每平台独立签名安装包/更新包、Host/Core兼容清单、SBOM/许可证/依赖扫描和SHA-256;高危可利用漏洞未解决则阻塞;发布密钥只在受控发布环境 |
| G5 用户链路 | E-01~05、平台能力/非目标/恢复说明、产品待决项已关闭;不把五项完成写成整个第三阶段完成 |
报告写入CI产物,包含commit、lock摘要、安装包摘要、测试ID、命令、时间、环境与失败日志;验收记录链接产物并标“待验收/通过/阻塞”。报告与签名产物保留至少180天。修改相关代码或依赖后相关门禁失效重跑;文档改动只需文档检查,不能捏造功能测试结果。
## 14. 非目标与仍需产品确认的事项
明确非目标:移动端、多人共享/CRDT、E2EE与跨设备秘密同步、历史GC、服务跨主机水平扩容、任意远程代码热注入、未通过平台的无沙箱例外;自动下载全部模型/CUDA;替代第三阶段其他UI/内容质量验收。通知推送可后续加入,轮询承担本次正确性与时延门禁。
以下在P0由产品/安全/运维共同确认,当前采用表中工程默认值推进设计,不把未答复当批准:
| 事项 | 本计划默认 / 未确认的影响 |
| --- | --- |
| 首发平台与支持声明 | Windows 11 x64先验收;macOS/Linux后续门,不能以缩小首发范围取消总阶段三平台要求 |
| 凭据体验 | 用户口令解锁,遗失需恢复备份或重新录入;自动解锁另案;旧Fernet清除必须显式确认 |
| 扩展兼容性 | 首发只支持能通过沙箱及broker的执行配置;需要任意网络/文件/子进程的包拒绝启用,需确认用户可接受范围 |
| 同步默认与隐私 | 按第10.2节默认/可选分类,服务器可见明文;默认每Vault 1 GiB配额沿原型,历史永久保留,满额需扩容不删历史 |
| 容量及运维 | 300 MiB基础包、性能基准、RPO24h/RTO30min与180天证据保留;确定实际部署成本和负责人员 |
| 排期及发布责任 | 依依赖门推进,不承诺未经估算日期;负责人、签名密钥保管、独立安全评审与值班必须落实到人 |
产品未确认不阻止本规划文档提交,但阻止相应生产范围对外承诺。后续实施以工作包拆PR,不在本次文档提交附带功能代码或用户数据。