# 内部OA运行与恢复底线 ## Goal 为 5–10 人内部 OA 建立一个主要开发者可执行的备份与恢复底线:数据库和附件始终成对备份、产物可校验、恢复默认只能进入明确的影子目标,并用合成数据证明可恢复;同时把真实内部实例的运行来源、角色职责、异机副本和 `/ready` 验收留在明确的外部授权门后。 ## Background - 当前可信状态仍是 `LOCAL_CANDIDATE_PASS`,不能据此宣称已部署或可以正式内用。 - 已有 `backup-db.sh`、`backup-storage.sh`、`restore-db.sh` 和 `restore-storage.sh`,runbook 也描述了 DB + 文件恢复,但尚无当前恢复演练证据。 - DB 恢复会执行 `pg_restore --clean --if-exists`;当前脚本没有影子目标确认,误指向真实库会产生破坏性后果。 - 文件恢复会直接解压到目标目录;当前不会拒绝非空目录,可能把旧文件和恢复文件混合。 - DB 与附件备份分别生成时间戳,没有共同批次清单、完整性校验和,无法稳定证明两份产物属于同一次恢复点。 - 本任务以小团队低维护为目标,不引入备份平台、对象存储、Kubernetes、云厂商锁定或复杂灾备编排。 ## Scope - 本地备份/恢复 shell 脚本的安全合同、成对备份入口、完整性校验与脱敏 manifest。 - 使用一次性 PostgreSQL 目标和临时附件目录完成合成数据恢复演练及负向测试。 - 更新现有试运行 runbook、checklist 和部署规约,明确影子恢复与真实恢复边界。 - 规划并保留真实运行 commit、访问边界、角色职责、异机副本及恢复后 `/ready` 的外部授权门;未授权前不执行这些动作。 ## Requirements - 提供一个简单的成对备份入口,复用现有 DB/附件脚本,以同一批次标识产生两份产物和脱敏 manifest。 - manifest 只包含批次号、相对文件名、大小、SHA-256、创建时间及安全的版本信息,不包含数据库 URL、Secret、真实附件名或完整绝对路径。 - 备份完成前验证 DB dump 可列出、附件 tar 可读取;任一部分失败时不得产出“完整批次”结论。 - DB 恢复必须先解析并显示目标数据库名,并要求调用者显式确认相同目标;默认仅允许 `shadow` 演练模式,且实际库名必须带 `shadow`、`drill` 或 `restore_test` 独立标记。本任务不开放生产恢复快捷开关。 - 附件恢复必须要求显式目标目录;默认拒绝非空目录,并在解压前检查归档条目,禁止绝对路径和 `..` 越界路径。 - 用完全隔离的合成数据库和合成附件完成本地往返演练,不读取或复制真实发票、合同、银行资料或生产数据。 - 恢复演练至少证明:DB 标记数据恢复、附件内容哈希一致、损坏备份被拒绝、错误确认值被拒绝、非空附件目标被拒绝。 - 更新现有 runbook/checklist,使正常备份、影子演练、真实恢复三种场景不再共用含糊命令;保持现有本地存储默认,不启用 MinIO。 - 将真实内部实例核查列为后续同任务外部门禁:运行 commit/访问方式/TLS、真实职责分配、异机副本和恢复后 `/ready` 均需另行批准和脱敏留证。 ## Acceptance Criteria - [x] 一条命令生成同批 DB、附件和脱敏 manifest,且完整性校验通过。 - [x] DB 恢复在缺少或不匹配目标确认时失败,不会执行 `pg_restore --clean`。 - [x] 附件恢复拒绝隐式目标、非空目录和危险 tar 路径。 - [x] 隔离合成数据演练证明 DB 与附件可往返恢复,并记录实际耗时作为本地 RTO 参考。 - [x] runbook/checklist 明确影子演练命令、生产停止条件、DB + 附件配对要求和异机副本要求。 - [x] 相关 shell 语法、QA 脚本、Trellis 治理、敏感扫描和 `git diff --check` 通过。 - [ ] 在获得单独授权后,确认真实运行 commit、访问边界、职责分离、异机备份及恢复后 `/ready`;未授权前保持未勾选且不宣称正式可用。 ## Out of Scope - 不连接生产数据库,不读取真实附件,不执行远程恢复、部署或公网检查。 - 不修改 Prisma schema、业务 API、权限模型、审批链或付款状态机。 - 不引入 MinIO、云备份 SDK、全量灾备平台、自动跨地域复制或 24×7 值守。 - 不在仓库提交备份文件、恢复数据、环境变量、凭证、真实账号或完整本地路径。 - 不在本任务完成企微调休、导航、报销或模块退役;它们属于后续批次。 ## Test Plan - `bash -n scripts/backup-db.sh scripts/backup-storage.sh scripts/restore-db.sh scripts/restore-storage.sh <新增脚本>` - 使用临时目录和一次性本地 PostgreSQL 目标运行合成数据恢复演练;若本机缺少 PostgreSQL/Docker,则记录为环境阻塞,不改用真实库。 - 覆盖损坏 dump/tar、危险 tar 路径、非空目标目录、缺少确认和确认不匹配等负向场景。 - `python3 ./.trellis/scripts/task.py validate .trellis/tasks/07-26-internal-ops-recovery-baseline` - `node scripts/qa/check-trellis-governance.mjs` - `node scripts/qa/check-sensitive-content.mjs` - `git diff --check` ## Key Decision 先改进可恢复性和防误操作,再接触真实环境。默认工作流只支持影子恢复;真实恢复不是普通脚本路径,而是需要停机、负责人确认、成对备份、回滚点和独立授权的事故操作。 ## Local Evidence - 已使用一次性 PostgreSQL 16 容器和合成附件完成多轮往返演练,最近一次本地参考 RTO 为 6 秒。 - 演练覆盖 manifest/校验和、错误确认、正式风格库名、损坏 DB、损坏 tar、非空附件目录、默认试运行附件目录和危险 tar 路径拒绝。 - 该证据不包含真实业务数据、真实运行实例、异机副本或部署后 `/ready`,因此最后一项验收保持未完成。 ## Live Read-Only Evidence - 获批只读核查结果见 `research/live-read-only-baseline.md`,结论为 `NO_GO_FOR_FORMAL_INTERNAL_USE`。 - 公开 HTTPS、TLS、HSTS 和 `/ready` 当前可用,但运行环境没有后端/前端构建标识,且远端迁移少于本地当前基线。 - 真实角色只有一个活跃账号,财务和付款复核候选均为 0,不满足 maker-checker。 - 只读核查时的历史 DB/附件备份约 55 天未更新、无 manifest,且与运行数据位于同一设备;获批后已生成新的 DB+附件+manifest 完整批次并复制到独立主机,两端校验一致。 - 当前代码已在获批后以普通快进方式发布到 upstream,来源门禁为 `PASS`;尚未执行角色修改、部署、迁移或真实格式影子恢复,因此最后一项验收继续保持未勾选。 ## Notes - Keep `prd.md` focused on requirements, constraints, and acceptance criteria. - Lightweight tasks can remain PRD-only. - For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.