Files
multi-simadmin/docs/product/project-charter.md
T

22 KiB
Raw Blame History

API-first Multi SimAdmin 项目章程

文档状态:Phase 0.1 基线
冻结日期:2026-07-15
上游证据基线:3899/SimAdmin@58e220411d6599609f0eeda01eb7016e9212f970
适用范围:Multi SimAdmin V1 的产品、API/BFF、Web 控制台、测试与迁移决策

Phase 0.1 文档导航:项目章程(本文)|现状审计角色与流程信息架构

1. 立项结论

现有产品是“状态卡片墙 + 原站 iframe + 手工 API 请求框”,不能支撑多实例日常管理。项目从产品信息架构和控制面契约开始重构,建设一个 API-first、能力感知、安全可确认、任务可追踪、操作可审计 的多实例 SimAdmin 控制台。

“API-first”在本项目中的可验收含义是:

  1. 所有核心管理能力由明确建模的控制面 API 提供,不以 iframe 作为功能依赖。
  2. 固定上游基线的每个业务操作均进入 OperationRegistry,状态只能是“已支持、版本不支持或有理由暂缓”,不得静默遗漏。
  3. 用户通过业务页面和结构化表单完成操作,不手写 HTTP 方法、路径和任意 JSON。
  4. UI、BFF 授权、能力目录、OpenAPI 和契约测试共享同一操作事实源。
  5. 所有改变设备状态的请求都经过风险分级;R3 全部 Job 化;R2 默认 Job 化,仅允许 OperationRegistry 对经理由与测试证明可同步安全执行的单目标操作显式声明例外;批量 R2 仍全部 Job 化。R2/R3 均经过预检、专用确认和审计。

2. 问题陈述

当前系统存在以下不可通过局部美化解决的问题:

  • 主要路由、DOM 拼接、状态和异步行为集中于 public/app.js,模块边界与可测试性不足。
  • 默认卡片墙强调装饰而非异常定位、筛选、比较和批量管理。
  • iframe 会被上游 X-Frame-Options、CSP、跨站 Cookie 阻断,无法作为可靠入口。
  • API 工作台把方法、路径和 JSON 正确性转嫁给操作者,且无法表达字段约束、影响范围与兼容性。
  • endpoint 分散在前端、catalog、proxy policy 和 status service,存在漂移。
  • 当前代理只覆盖 36 个读路径、15 个写路径;上游基线确认有 100 个路径、117 个业务操作(GET 50、POST 62、DELETE 5)。
  • 缺少 capability、版本差异、部分失败、陈旧数据、Job、审计和历史状态的一致模型。

3. 产品愿景与原则

3.1 愿景

操作者无需理解 SimAdmin 内部 endpoint,即可从一个高密度控制台发现异常、进入正确模块、安全修改单台或多台实例,并能回答“谁在何时对哪台实例做了什么、结果如何”。

3.2 强制原则

  • 异常优先: Fleet 默认表格按不可达、认证失败、退化、陈旧等异常可见且可筛选。
  • 能力驱动: 不支持的能力不显示为可点击动作;必要时保留解释性占位,不用 - 假装正常。
  • 目标明确: 每个写操作都显示实例、当前值、目标值、字段 diff、影响和风险。
  • 默认拒绝: 未注册 operation、未知方法/路径、认证端点通用代理均拒绝。
  • 部分结果真实: 批量动作逐实例给出成功、失败、跳过及原因,不制造“全部成功”。
  • 凭据服务端化: 密码、Cookie、token 不进入浏览器响应、日志、审计或 fixture。
  • 高密度但可访问: 主要工作流兼容键盘和窄屏;视觉不能牺牲错误、焦点和操作可达性。

4. 目标与验收指标

编号 V1 目标 验收证据
G1 多实例可发现、可筛选、可比较 /fleet 支持搜索、状态/认证/版本/标签/运营商/信号/异常筛选,支持排序、列显隐、分页、多选
G2 完整掌握上游能力 固定 commit 的 117 个操作在 registry parity 测试中 117/117 对齐,无重复 operationId
G3 核心操作不依赖 iframe/手写 JSON 13 个上游模块均有结构化业务入口;原站仅为 noopener noreferrer 新窗口链接
G4 状态可诊断 UI/API 区分 online、offline、auth-required、degraded、unknown,以及 fresh、stale、loading、error、unsupported、partial
G5 写操作安全 R1 显示字段 diff;R2/R3 有预检、绑定目标的一次性确认令牌、结果追踪和审计;R3、批量 R2 及无同步安全例外的 R2 有 Job 证据
G6 多实例请求受控 scheduler 具备全局/单实例并发、TTL、jitter、backoff、deadline、取消和熔断;页面刷新不扇出 117 个请求
G7 凭据不泄漏 secret redaction 契约与安全测试证明 password/cookie/token 不出现在响应、日志、audit、snapshot、fixture
G8 真实可用 Phase 8 在冻结的真实实例版本/响应基线上逐模块完成读验收,并完成 cellular 至少一个 R1、Notifications channel test、Automation task test,以及专用测试实例上的 service restartR3)真实写验收;不安全的 OTA apply、删除/通话类动作允许契约+模拟,但必须明确标为模拟,不得计入真实写
G9 响应式与可访问 可见 Chrome 在 1440、1024、768、390、320 宽度无页面横向溢出、遮挡、重叠或不可达动作;关键流程键盘可完成
G10 可安全切换 新旧并行、只读 shadow comparison、数据库备份、独立端口和一键回滚均有演练记录

5. 范围

5.1 V1 范围内

  1. 实例添加、编辑、删除、标签、连接测试、认证测试、会话登录/登出。
  2. Fleet 汇总、状态采集、搜索筛选、异常定位、多选和受控批量动作。
  3. 实例模块:overview、cellular、device-network、messages、calls、esim、notifications、automation、ota。
  4. 跨实例模块:jobs、audit、settings/instances、settings/system。
  5. 117 个上游操作的 registry、schema、风险、超时、兼容性和 UI 策略建模。
  6. 控制面 /api/v1、OpenAPI 3.1、统一错误、分页/排序/filter、revision、partial result。
  7. Job、逐实例 job item、取消/重试、重启恢复;审计与敏感字段脱敏。
  8. SQLite 持久化,macOS Keychain 优先保存 secret reference;旧配置预览后幂等导入。
  9. SSE 推送实例状态、Job 和 audit 摘要。
  10. 单元、契约、集成、组件、E2E、安全、可访问性和真实浏览器验收。

5.2 V1 非目标

  • 公网 SaaS、租户隔离和多用户 RBAC。
  • 任意 REST 客户端、任意 URL/路径代理或插件脚本执行器。
  • iframe 集成、复制上游完整页面、自由拖拽 dashboard。
  • 复杂告警规则引擎、短信/语音业务运营平台。
  • 跨地域 HA、集群调度和云端 secret vault。
  • 未经 registry 声明的“专家模式”绕过风险确认。
  • 在 Phase 0.1 修改生产代码、删除旧实现、切换 8788 或迁移真实数据。

6. 用户与核心任务

用户 首要任务 成功判据
日常运维员 找到异常实例并恢复连接/服务 2 分钟内从 Fleet 定位异常,进入正确模块,看到结果或 Job
网络/设备工程师 诊断蜂窝、射频、WLAN、APN、eSIM 当前值、能力、版本、采集时间和错误证据同时可见;高风险变更可回溯
系统维护者 配置实例、控制轮询、升级、排障 实例 CRUD 和导入不泄漏凭据;OTA/重启有预检、任务与回滚提示
审计/故障复盘者 解释一次变更及部分失败 可按时间、实例、operation、风险、结果、requestId 检索完整摘要

详细流程见 personas-and-workflows.md

7. 状态与风险政策

7.1 实例汇总状态

优先级从高到低:

  1. offlinehealth/network 不可达或 deadline 超时。
  2. auth-required:可达但认证缺失、失效或 401。
  3. degraded:可达且可认证,但一个或多个关键采集项失败/熔断。
  4. online:关键采集项成功,且未超过 freshness TTL。
  5. unknown:尚未采集、版本/响应无法判定或初始化中。

汇总状态不得覆盖采集项状态;详情必须展示每项的 fetchedAtdurationfreshnesssupporterrorCode 和 HTTP status。

7.2 风险等级

等级 定义 最低交互/执行要求
R0 严格只读查询,包括 health 与 auth-status;不得提交凭据或改变服务端会话 无确认;受 scheduler、timeout、cache 控制
R1 可逆设置 结构化校验,显示当前值→目标值和目标实例,显式提交,写入 audit
R1-S R1 的会话敏感子类,不是第五个风险等级credential verify(提交一次性临时密码)、login、logout;只改变专用认证会话,不改变设备 setup/password 仅走专用会话 BFF;按 actor+session+instance 限速;审计 actor/session、实例、operation、时间、结果、requestId 等元数据但绝不记录 secret/凭据正文或摘要;超时、401、重连后均禁止自动重放
R2 服务影响 capability/认证/当前状态预检,专用确认,一次性 token,默认 Job 化,逐实例结果;仅单目标 operation 可由 OperationRegistry 显式标记 syncSafe=true 例外,且必须同时记录理由并有同步执行安全性/超时/结果落点测试;批量 R2 不允许例外,仍全部 Job 化
R3 破坏性或高危 R2 全部确认与预检要求 + 强确认文案/目标复述 + 不可逆或中断说明 + 失败恢复建议;无同步例外,全部 Job 化

认证 setup/password 等设备凭据改动是 R3 专用流程并全部 Job 化,不进入通用上游代理;待 Phase 0.2 固定上游请求/响应形态后只能细化 schema、前置条件和恢复步骤,不得降级为 R1-S 或通用代理。实例配置中的本地 SecretStore reference set/preserve/clear 仍是 R1,必须与设备密码改动明确分开。

7.3 确认令牌唯一规范

本节是 V1 确认令牌的唯一规范;其余 Phase 0.1 文档只能引用本节,不得维护字段子集。R2/R3 prepare 生成的短期一次性 token 必须不可篡改地绑定:actorId、控制台 sessionId、实例 stable ID、实例 config revision、canonical origin、operationId、HTTP method、canonical path/query、request body digest、规范化 content type、expiry 和高熵 nonce。

  • token 只允许提交一次且短期有效;服务端在第一次执行尝试入口即原子消费,即使随后发生字段 mismatch、过期、revision 改变或执行失败也不得恢复或复用。
  • canonical path/query 必须使用单一编码/排序规则;执行端重新计算全部绑定字段并常量时间比较摘要。目标、表单、actor/session 或配置变化均要求重新 prepare。
  • token 不提供幂等重放语义;网络结果未知时读取目标状态或创建人工核实结果,不自动再次执行。token 本身及 secret 不写日志/audit。

7.4 Preparation、Job 与 Attempt 固定模型

prepare 先创建独立、短期的 Preparation 记录(prepared | consumed | expired | invalidated),保存脱敏预检结果和确认令牌引用;它不是 Job。确认请求原子消费 token/Preparation 后,才允许为需 Job 化的操作创建 Job,因而 Job 状态机不含 preflightingawaiting-confirmation

  • Job 有稳定 jobId,状态为 queued | running | cancelling | succeeded | partially-succeeded | failed | cancelled | unknown-result。状态机只允许 queued → running | cancelledrunning → succeeded | partially-succeeded | failed | unknown-result | cancellingcancelling → cancelled | failed | unknown-resultsucceeded | partially-succeeded | failed | cancelled | unknown-result 均为终态。Job 一旦进入终态,状态、结果、JobItems、Attempts 和事件历史严格不可变,不得以 retry、恢复或对账改写。
  • 每次实际执行是一个有稳定 attemptId 的 AttemptAttempt 的参数摘要、transport tries、事件和结果一经写入即不可变。用户发起的 retry 永远创建新 Job:新 Job 使用新 jobId、独立状态和独立 AttemptretryOfJobId 指向本次重试的直接来源 JobrootJobId 指向 lineage 中最初 Job;来源 Job 不新增 Attempt、不重新打开终态。
  • 批量父 Job 包含稳定 JobItems。重试失败 items 时创建新的父 Job,只复制用户明确选择且 OperationRegistry 判定可重试的来源 items;每个新 JobItem 使用新 jobItemId 并以 sourceJobItemId 关联直接来源。成功项绝不复制、重放或因父 Job 重试而执行;未选择、不可重试和 skipped 项也不进入新父 Job。
  • 新 Job 的 SSE 事件与 audit 执行记录关联新 jobId + attemptId,批量项事件还关联新 jobItemId;同时记录 retryOfJobIdrootJobId 和适用的 sourceJobItemId,使 lineage 可双向追踪。Preparation/audit 可用 preparationId 关联,但不能伪装成 Job 阶段。
  • 仅在 Job 尚未终态时,registry 明确允许的网络级内部重试才可在同一 Attempt内有限执行;必须受次数/deadline/backoff 政策约束并逐次记录 transport try。该机制不得用于非幂等操作,也不得被 UI/user retry 调用;网络结果未知的非幂等操作进入 unknown-result 或人工核实,不自动重放。

7.5 已登记实例 origin 与 SSRF/LAN 授权

出站 adapter 只能连接管理员显式创建且通过校验的实例 canonical origin;任何页面参数、redirect、上游正文和 operation 参数都不能改变目标。仅允许 http/https,authority 必须包含明确端口(省略时规范化为协议默认端口),拒绝 userinfo、fragment、空/歧义 hostname、非规范或歧义 IP 写法(包括整数/八进制/十六进制 IPv4、混合编码与 zone-id)。

V1 因本机控制台用途,默认允许 loopback 与 RFC1918 LAN 地址,但仅限已登记 origin 的精确 scheme+host+port authority,绝不允许借此扫描任意 loopback/LAN 端口。默认拒绝 link-local、unspecified、multicast、broadcast、云 metadata 地址、公网 IP,以及 IPv4-mapped IPv6、NAT64、6to4/Teredo 等转换/绕过类别;公网放行只能是未来显式政策,V1 不实现。IPv6 私网若未来支持也必须显式列入策略,不能由“非公网”推断允许。

  • hostname 每次连接前解析,所有 A/AAAA 结果必须全部属于允许类别;解析结果与实例 revision 一起用于连接,并将实际 socket 目标固定为已验证地址,连接建立时再次验证实际 peer address,禁止 DNS rebinding。
  • redirect 默认禁用;若某 operation 经 registry 明确需要,逐跳限制次数并重新执行完整校验,且每跳必须与登记 canonical origin 同 origin,不能用 redirect 改 authority。
  • transport 忽略 HTTP_PROXYHTTPS_PROXYALL_PROXYNO_PROXY 等环境代理;HTTP Host 与 TLS SNI 必须由登记 origin 构造并与其一致,不接受调用者覆盖。
  • 安全测试矩阵至少覆盖允许的已登记 loopback/RFC1918 精确端口,以及未登记端口、协议/userinfo/fragment、各类非规范 IPv4、IPv6 转换、link-local/unspecified/multicast/broadcast/metadata/公网、混合 DNS 结果、DNS rebinding、redirect 跨 origin、代理环境变量和 Host/SNI 覆盖;拒绝用例必须证明未产生外连。

7.6 三个正交页面状态

页面状态固定为三个不可互相覆盖的维度:请求执行 idle/loading/success/error、数据新鲜度 fresh/stale/expired/unknown、能力支持 supported/unsupported/auth-required/degraded/unknown。实例汇总状态与这三维并存;详细呈现和组合规则见信息架构 5.1。领域/API 枚举只能使用 fresh,中文显示为“最新”;禁止 API/数据库枚举使用 latest

8. 交付物与阶段门禁

8.1 Phase 0.1 交付物

8.2 Phase 0.1 验收清单

  • 四份文档均为中文且互相链接,包含目标、非目标、状态、风险、路由和模块边界。
  • 审计清单逐项标为保留迁移、重写或废弃,并有理由与退出条件。
  • 用户流程覆盖实例接入、Fleet 定位、模块读写、批量动作、Job、audit 和错误恢复。
  • IA 包含全部目标路由及页面 × API 模块 × 状态 × 风险验收矩阵。
  • 上游基线 commit 和“100 路径/117 操作”只作为 Phase 0.2 待 parity 验证的冻结事实,不宣称已完成 registry。
  • git diff --check 通过;本任务不修改生产代码,规格与质量复审通过前不提交。

8.3 后续阶段门禁

门禁 进入条件 禁止带入下一阶段的问题
Phase 0 → 1 章程/审计/流程/IA 评审;117 操作证据矩阵与脱敏 fixture 策略完成 endpoint 不明、风险未分级、页面归属冲突
Phase 1 → 2 workspace、strict typecheck、OpenAPI、统一错误与 Fastify 骨架通过契约测试 无 schema API、无 requestId、控制面路径漂移
Phase 2 → 3 migration 可重放;旧配置预览/幂等导入;SecretStore 无泄漏;实例 CRUD/连接测试通过 明文 secret、不可回滚迁移、含糊 password 更新
Phase 3 → 4 registry 117/117capability 五态;安全执行管线 replay/mismatch/expiry 测试通过 任意代理、unknown 默认允许、R3 同步执行、批量 R2 同步执行、或 R2 无 syncSafe 理由/测试的同步裸执行
Phase 4 → 5 scheduler 限流/退避/熔断;结构化状态;SSE 重连续传测试通过 页面触发请求风暴、空 catch、陈旧数据无标识
Phase 5 → 6 AppShell、Fleet、实例 CRUD、capability 导航完成多宽度/键盘验收 Animal Island/iframe/通用 API 工作台残留为主流程
Phase 6 → 7 每模块至少一读一写契约与 E2E;真实实例读验收;风险表单符合政策 模块仅展示 raw JSON、unsupported 冒充空值
Phase 7 → 8 Job 可恢复、批量逐项结果、audit 可检索且脱敏 部分失败被折叠、任务重启丢失、敏感字段入库
Phase 8 → 9 全测试、安全审计、真实 Chrome、独立规格/安全复审无 blocker/major 用 mock 替代真实验收、可访问性或安全缺口
Phase 9 正式切换 shadow comparison、备份恢复、回滚演练,新版独立端口稳定 直接覆盖 8788、先删旧版再验证

8.4 可重复验收协议与冻结责任

  • 100 实例 fixture Phase 0.2 由测试负责人冻结版本化 seed、生成器版本和期望结果;固定包含 online/offline/auth-required/degraded、三维页面状态、能力差异和批量部分失败。相同 seed 必须生成相同 stable ID、origin、数据和排序。
  • Fleet 计时: Phase 8 在指定浏览器/硬件、冷启动 fixture 后开始;从 /fleet 首个可交互帧且筛选控件 enabled 时为 T0,到目标实例行可见且目标异常单元格可操作为 T1。记录 T1-T0、浏览器/硬件、fixture hash;阈值由 Phase 0.2 性能负责人冻结,Phase 0.1 不伪造数值。
  • 导航计数: 从 Fleet T0 后第一次用户 click/keyboard activation 起计;筛选/搜索提交算一次,分页或展开算一次,进入目标模块算一次,滚动不算导航但受下一条约束。100 实例 fixture 下应用目标筛选后,目标必须出现在首个 viewport,或仅需一次明确分页/行详情跳转后可见;禁止以逐卡/逐行滚动寻找目标作为通过条件。进入失败采集项详情总计不超过 3 次导航。
  • 真实写清单: Phase 8 必测 cellular 一个 R1、Notifications channel test、Automation task test,以及专用可恢复测试实例的 service restart(R3);均保存上游前后读取、版本、响应摘要、jobId/attemptId、audit 和恢复证据。OTA apply、eSIM/消息/通话删除等若环境不安全,只做契约+模拟并显著标记 SIMULATED,不得冒充真实。
  • 版本/响应冻结: Phase 0.2 的上游取证负责人固定 SimAdmin commit/版本、每个操作的脱敏真实响应 fixture、schema 和真实/模拟分类,测试负责人固定环境与指标阈值;Phase 8 QA 按冻结物执行并报告 fixture hash、环境、原始结果和偏差。版本变化必须重新取证和审批,不得静默更新 golden。

9. 依赖、约束与风险

  • 上游无正式 OpenAPI:以固定 commit 的路由、handler、model、Bruno 和上游前端交叉取证。
  • 上游版本差异:capability discovery + compatibility adapter + fixture,不用空值吞差异。
  • 多实例扇出:并发限额、TTL、jitter、backoff、deadline、取消、熔断和 SSE。
  • 网络盘路径:代码可留在现路径;运行期 SQLite、缓存和 secrets 应落本地数据目录。
  • OTA 大文件:专用上传流和进度,不放入普通 JSON proxy;普通 body limit 不作为产品方案。
  • UI 偏离运维需求:先验收 IA、表格与完整工作流,再验收视觉。

10. 决策与变更控制

以下变更必须更新本章程、IA 或 registry 并经过规格与安全复审:

  • 新增/移除一级路由或上游操作;
  • 改变风险等级、确认策略或 batchable 属性;
  • 改变 secret 存储和日志/audit 字段;
  • unsupportedauth-requireddegraded 合并为模糊错误;
  • 引入通用代理、iframe、绕过 Job 的 R3/批量 R2 快捷入口,或未由 OperationRegistry 以理由和测试明确证明 syncSafe 的同步 R2 入口。

11. 完成定义

V1 只有在 G1–G10 全部有可重复证据、117 个操作没有静默遗漏、真实实例和真实浏览器验收通过、且 8788 切换具备回滚时才称为“可使用”。单纯页面完成、接口返回 200 或单元测试通过均不构成项目完成。