架构决策摘要
本页汇总已经裁定、实现不得偏离的架构结论。它按主题组织,不按时间或内部登记顺序排列;内部编号、讨论记录与审计材料不在本站出现。
「已定」表示当前实现必须遵守,变更须先修改设计约定并随框架升版;「待裁定」表示尚无结论,实现不得猜测填充。
大插件与 RPM
| 决策 | 结论 | 理由 |
|---|---|---|
| 大插件集合 | 16 个大插件即最终骨架,第三方不得新增;变更走框架升版 | 保持结构稳定,给扩展单元一个长期不变的靶子 |
| RPM 生成 | 由构建脚本扫描源码自动生成,禁止手写,内嵌于每个产物,运行期不可变 | 手写清单必然漂移,漂移的清单比没有清单更危险 |
| RPM 的用途 | 同时作为准入门禁、审计参照与 Mod 兼容判定的共同来源 | 三份权威共用一份事实,避免各自维护 |
| 热更新边界 | hotfix 只能更新小插件、数据包、资源包与 Mod;大插件与 RPM 必须新构建 | 大插件变更等于框架变更,不能借热更新绕过 |
| 内置包 | 内置包登记进 RPM 并绑定归档 hash;hash 一致免用户批准,不一致拒绝启动;只能是数据包或资源包 | 信任锚与根插件同根,避免内置内容成为免检后门 |
| N-1 兼容 | 声明 ^N 契约的单元可在框架 N 与 N+1 上运行,N+2 起不再接受;实现位只能新增,弃用须保留一个完整版本周期 | 给 Mod 明确的兼容窗口,避免接口频繁消失 |
扩展四档与写边
| 决策 | 结论 | 理由 |
|---|---|---|
| 四档划分 | 数据包、资源包、插件、Mod 按「是否含代码」与「是否产生写边」区分 | 分类可机械判定,不依赖作者自述 |
| 内容与表现 | 删除测试决定边界:删掉后故事讲不下去的是内容,画面变差但故事仍成立的是表现 | 给数据包与资源包一条可操作的判据 |
| Plugin 与 Mod | 写边是唯一分类依据;清单声明的 kind 必须与内容计算出的分类一致 | 防止把改写行为伪装成普通插件 |
| 同 realm | 插件与 Mod 都是同 realm 受审代码,没有技术隔离;真正的边界是宿主持有的能力、隔离 realm 与远端信任链 | JavaScript 同 realm 内无法全面阻止任意代码 |
| policy | 由被作用目标自身声明,取 closed/public/trusted,只决定准入,不在运行期协商 | 外部性由被作用方承担 |
| 两类插槽 | 机制插槽供 Mod 改写,实现位供 Plugin 注册;实现位分 single 与 multi,实现名唯一 | 不同扩展意图使用不同入口,冲突可机械裁决 |
| guard 时机 | guard 只在装载时触发,不参与运行期热路径,不缓存判定结果,不维护依赖账本 | 避免成为扼流点或状态副本 |
| 装载后核验 | 实际发生的写边必须与声明一致;声明了却没有发生同样判为不符并卸载 | 让清单声明对实际行为负责 |
| ctx 裁剪 | intercept 与 isolate 域内 provide 的名字须预先声明;isolate 外 provide 一律报错 | 流程约束的辅助,不是安全边界 |
| 代码入口 | 代码单元只能是 code/ 下单个打包后的 ESM,包内模块相对 import 不提供 | 两端宿主都只暂存入口,避免隐式模块图 |
信任与签名
| 决策 | 结论 | 理由 |
|---|---|---|
| 签名范围 | release 构建下代码单元必须签名;数据包与资源包不要求签名 | 代码可执行,数据不执行;安全关键 UI 另有防线 |
| 四态判定 | 清单内通过、清单外通过、核验失败、未签名严格区分,不得合并 | 「被篡改」与「没有签名」是不同威胁,处置也不同 |
| TOFU | 每个发布者名只允许一个签名;首次装载记录名称到公钥指纹的映射 | 身份判定不依赖人类可读名称 |
| 同名异钥 | 已记录密钥的一方照常,新来者挂起等待裁决;裁决须展示两个完整指纹与是否在清单内 | 抢占先机也必须被用户看见 |
| 撤销 | 接受新清单当次起拒绝被撤销密钥的新装载;已装载单元继续运行,不卸载、不撤销作用域 | 避免一次撤销打断正在进行的游玩 |
| 未签名批准 | 首次批准绑定归档 hash;hash 变化须重新批准,并展示修改时间、写入方(可查时)与逐文件 diff | 用户对「装了什么」有知情权 |
| 安全关键 UI | 只用内置字体与样式,不经资源管线;assets/sui/ 为保留命名空间 | 恶意字体可用同形字形伪造信任信息 |
| 名称防护 | NFC 规范化,拒绝控制、格式、未分配与私用区字符,做同形与混合脚本检测 | 显示名不能成为身份判定依据 |
| 信任存储 | 框架之外的独立数据库;装载器每次只取与待判单元相关的快照,拿不到整库 | 用户同意不能由扩展代码伪造 |
| 根密钥与清单 | 根公钥构建期内嵌;签名清单单调序号,根签名有效且序号更大即自动接受;离线过期沿用并提示 | 防回滚,同时保留离线可用性 |
| trusted 写入 | 须清单内签名或逐条用户同意;hook 生效与摘除都记入审计 | 更高信任必须显式授予 |
翻译层与隔离
| 决策 | 结论 | 理由 |
|---|---|---|
| 翻译层定位 | 框架唯一的输入侧;框架自身不识别任何特定输入源的格式 | 保持内部协议中立,换输入源不改内核 |
| 三件分离 | 翻译规则是数据包,翻译器是插件,翻译产物是数据包与资源包 | 规则、执行与产物可分别更新、分别判定 |
| 翻译边界 | 翻译叙事与玩法数值;规则由框架实现,翻译器不翻译规则 | 避免把外部规则语义带进内核 |
| codec 隔离 | translate:codec@1 条目只在专用 Worker 中执行,只有隔离字节通道,没有 ctx、DOM 与网络 | 把不可信解码代码的影响面限制在独立 realm |
| codec 限制 | 隔离 codec 只登记 codec 实现位,不得混入主 realm 入口或其他写边 | 避免借隔离入口取得普通插件能力 |
| 格式声明 | codec 登记项须声明非空、去重的 formats,运行时与导出核对,不一致即作业失败 | 清单与实际能力必须一致 |
| 二进制解析 | 文本与 JSON 映射在 Worker 的 JS 中完成;二进制容器与编码交 Rust 同源编译的 WASM;缺少能力即报不支持 | 不用近似行为冒充解码 |
| 预算 | 墙钟、输入与输出分别计量并强制;默认 30 分钟、输入 1 GiB、输出 1 GiB;重试可调高到 30–240 分钟、输入与输出各 1–8 GiB | 翻译是不可信长任务,需要硬边界 |
| ZIP 输入 | 只支持 store 与 deflate;拒绝其他方法;中央目录最多 64 MiB,单条目的压缩数据与解压结果各最多 256 MiB,解压比上限 100:1;输入预算按本地文件头与压缩数据的读入量、解出量取大者计费,读取等待队列最多 64 项 | 防压缩炸弹与隐性超预算 |
| 产物边界 | 写出不授予信任、不自动装载;用户逐个选择后仍走普通准入 | 写出能力不等于安装能力 |
| 内容归属 | 特定输入源的规则、翻译器与内容产物不进入框架仓库;仓库只放通用 Translate 与接口 | 仓库只放框架自己的东西 |
资源与存档
| 决策 | 结论 | 理由 |
|---|---|---|
| 资源寻址 | VPath 是全局唯一寻址方式;来源栈 builtin 最低,后装载的资源包覆盖先前的 | 与文件系统、协议、平台解耦 |
| 覆盖语义 | 资源包按路径整体替换,不合并;控件皮肤只通过白名单属性调整外观、位置与尺寸 | 表现可换,事件绑定、键盘顺序和安全关键界面不被资源包改写 |
| 资源句柄 | 只交付句柄,不暴露 URL;字节每个句柄独立副本,JSON 深冻结;重复释放无效果 | 防止插件借共享对象绕过内核 |
| 逐次复核 | pack: 读取沿用已核验读取器并在每次交付前复核 SHA-256;核验后变化即拒绝 | 阻止核验后替换内容 |
| 缓存键 | 来源包稳定 ID、版本、VPath 与解码器版本共同构成缓存键 | 卸载、换包或换解码器都能精确失效 |
| 资源预算 | 单文件不超过 256 MiB,解码后单个资源不超过 512 MiB,缓存总量不超过 512 MiB | 给内存占用一个明确上限 |
| 存档默认 | 两端默认使用外壳源的 IndexedDB;槽位含位置、旗标、随机状态、访问记录与涉及单元 | 网页与桌面行为一致 |
| 已读记录 | visited 必须是稠密字符串数组,最多 1,000,000 条,每项 UTF-8 不超过 1 KiB | 防止稀疏数组与超长键造成内存和存储失控 |
| 章节恢复 | 有章节身份时从恢复位置继续并自动接下一章;旧存档没有该字段时只继续当前图;已记录但找不到的章节明确报错 | 不反推、不猜测章节 |
| 旗标归属 | 按清单显式声明的命名空间归属;core/sui 保留;同一命名空间只允许一个已装载单元声明 | 写入者不能自报归属 |
| 可清理性 | 代码单元必须声明 none/closed/public;用户进度始终按 closed 处理,声明不一致时告警并按 closed | 清理不能碰用户进度 |
| 孤儿处理 | 写入时由 Save 登记归属;boot:settled 后提示,用户可关闭或调整;损坏的登记一律不清理 | 宁可留残留,也不误删仍在用的数据 |
| 缺失单元 | 读档时只提示,不阻止;旧存档中缺失单元的旗标保留 | 进度可用性优先 |
平台与宿主
| 决策 | 结论 | 理由 |
|---|---|---|
| 功能基线 | 网页是功能基线,Tauri 只补浏览器做不到的差异 | 同一套代码两端运行,网页不被当成残废版 |
| 核心层约束 | src/core/ 与 src/shared/ 只依赖浏览器能力,不判断运行环境;宿主差异只在 src/host/ 与 src-tauri/ | 避免核心层分叉 |
| 环境判断 | 运行环境只在 boot 判断一次 | 防止同一会话出现两套行为 |
| 契约一致 | 两端使用同一份契约与同一份 RPM;「已完成」要求两端契约测试都通过 | 不以单端通过代替验收 |
| 外部包来源 | Tauri 扫描应用数据目录下的 packs/,另提供原生文件选择器;网页端用文件选择器逐文件选目录或选 ZIP | 各平台用自己可靠的方式取得输入 |
| 敏感能力 | 凭据与敏感能力放 Rust 侧;浏览器做不到的能力如实报「不支持」 | 不拿近似行为冒充原生能力 |
| 网页写出能力 | 只有 OPFS 与 Web Locks 同时可用时提供翻译产物写出;缺少任一项就明确禁用 | 不把不具备原子互斥的存储冒充可靠落盘 |
| 启动期限 | 从读取 RPM 到内置包就绪共享 60 秒;超时以不确定状态拒绝并逆序清理 | 防止半启动资源长期占用 |
| 网页发布源 | 网页构建必须显式提供不同的 HTTPS 外壳源与信任页源,并生成 CSP 文件 | 独立源和响应头策略不能依赖部署猜测 |
| 响应式 | 桌面与移动同时响应式,设计基准为横屏 | 一套界面覆盖两种使用场景 |
| 后端切换 | 装载开始时选定后端,本次准入、裁决、能力创建与执行固定使用同一后端;已装载单元不迁移 | 一次装载内行为可预期 |
| 内置外壳 | 外壳由 boot 挂载,不属于 Platform 根插件,因为控制面不进普通 ctx | 装卸载与信任决策不能被扩展单元调用 |
| 安全关键 UI 通道 | 信任页独立源,主题只传 light/dark 枚举值,不接受样式内容 | 减少可注入面 |
界面与皮肤
| 决策 | 结论 | 理由 |
|---|---|---|
| 界面导航 | 内置主页、独立设置与剧情界面由导航栈驱动;Android 返回键进入与 Esc 相同的路由 | 由当前界面与模态状态决定返回行为 |
| 实现选择 | 用户选择优先于数据包默认实现,再回落到内置实现;代用户选择只属于控制面 | 包可声明默认值,不能冒充用户选择 |
| 控件皮肤 | 按控件名及实例段回退逐级叠加;数值使用 CSS 像素,界面层不做设计画布缩放 | 控件尺寸与实际布局保持可预测 |
| 交互尺寸 | 可见图形可小于 44 × 44,透明可点补足区满足交互下限,位置按实际可用区域夹回 | 外观可调整,触控区域仍可用 |
| 内置默认皮肤 | 随内置包交付,未设置偏好时首启套用;用户显式不换皮的选择持续保留 | 初始外观与用户偏好可分别追踪 |
| 安全关键界面 | 皮肤不作用于独立源信任页 | 信任裁决的外观不由扩展内容控制 |
具体字段与回退顺序见 Platform。
创作与运营
| 决策 | 结论 | 理由 |
|---|---|---|
| 编辑器模型 | 编辑器直接复用运行时 IR 与 VM,不另造文档格式 | 编辑预览与真实播放一致 |
| 构建目标 | editor 构建含 Author 根插件,游戏运行时不含;两份 RPM 各自计算清单哈希 | 创作能力不进入玩家运行时 |
| 草稿与校验 | 允许保存未通过校验的草稿;编辑与预览不触发校验;完整播放或正式输出必须校验 | 草稿流程与正式产物不能混为一谈 |
| Online 地位 | Online 保留在 16 个大插件内,作为后续阶段交付 | 在线与运营是玩法的重要部分,先留实现位 |
| 凭据归属 | 扭蛋等敏感凭据由宿主持有,不放进插件 | 插件代码不应接触长期凭据 |
| 无生态模式 | 不提供「无生态模式」构建档;release 下未签名代码单元一律拒绝 | 不让构建档制造虚假的安全感 |
| 未交付入口 | 尚未交付的功能入口显示但置灰,并标注「暂未提供」 | 不伪装为可用 |
| 产物装载 | 翻译产物与编辑器产物都只是文件;是否装载由用户逐个选择 | 写出能力不等于信任 |
仍待裁定
以下事项尚无结论。实现不得猜测字段、默认值或行为;需要时先登记结论再写代码。
| 事项 | 当前状态 | 已知边界 |
|---|---|---|
| 翻译提取中的变量拼接、模板表达式与 HTML 片段 | 待裁定 | 不擅自删变量,不新增运行时模板机制 |
| Battle 的具体战斗数值、默认结算顺序与具体机制条目 | 待裁定 | 运行载荷、规则管线交接与三个机制插槽的输入输出类型与显式输出校验已实现;具体玩法规则尚未交付 |
| 垃圾清理的三类阈值默认值与提示频率 | 待裁定 | 归属登记、孤儿标记与用户可关闭的机制已定 |
| 云同步下孤儿删除的传播规则 | 待裁定 | 随平台阶段处理;本地孤儿规则已定 |
| 内存包注册协议与部分变体选择的表达方式 | 待裁定 | 精确路径匹配与既有变体回退不受影响 |
| Online 的对外协议细节 | 待裁定 | 不据此实现端点或对外承诺 |
| 绕过 ctx 裁剪、在 RPM 之外挂载子树的防护手段 | 待裁定 | 同 realm 内无法阻止,不得宣称已守护 |