回填隔离备份恢复演练执行记录 [08-23-isolated-backup-restore-drill]

从孤儿 worktree(artifacts/isolated-worktrees/20260909-ops-preparation、
20260910-reimbursement-payment-integration)合并回填两轮从未提交的真实
执行记录:

- 2026-09-09 ARM 管理员会话完成首次隔离数据库恢复,回执 DB_RESTORE_PASS,
  66 项迁移精确匹配、12 类关键表可读。
- 2026-09-11 第二次配对恢复,AC1/AC3/AC6 勾选完成;对发票池一条历史缺失
  附件做 SHA-256 比对、来源元数据核对、生产/ARM 归档/周检查点全量搜索,
  均未命中,按用户决定非破坏性标记、暂不追查(AC4 仍未过)。

无产品代码改动,无生产/ARM 操作;仅补齐 Trellis 任务文档与既有证据记录,
使 in_progress 状态反映真实已发生的执行历史。

Co-Authored-By: Claude Sonnet 5 <[email protected]>
这个提交包含在:
2026-09-25 19:45:18 +08:00
共同撰写人 Claude Sonnet 5
父节点 b1d25d3c99
当前提交 5be9f44445
共修改 4 个文件,包含 308 行新增和 4 行删除
@@ -1,5 +1,15 @@
# 隔离备份恢复演练设计 # 隔离备份恢复演练设计
## 2026-09-11 简化执行优先
附件失败诊断不改变准入:逐表保留原校验器,额外只输出缺文件、大小/摘要、路径/编码/行格式、非普通文件、外部存储和读取失败等白名单计数,并交叉核对原校验器的总数。不排除测试、归档、留痕或重复引用。所有表核对结束再执行同一失败门禁,避免只知道首张表失败;解包和普通文件数量验证、已完成 DB 验证须在附件失败前写入回执,不被笼统 NOT_TESTED 掩盖。无任何实际路径、记录 ID、文件摘要或内容进入诊断输出。
本轮遵循 PRD 最新收敛:固定摘要的一次性脚本内嵌已发布的 DB/附件恢复脚本和附件校验器,普通账号暂存,管理员在自己的终端输入一次 sudo 密码后执行;校验的是读入内存并将被执行的同一份字节,不采用先校验再以 root 重新读取文件的两次读取方式。没有长期安装器、sudoers 修改或通用 root shell。
仅本轮随机标签的无网络 PostgreSQL 容器、专属卷和私有目录可写;最终 TCP 查询连续成功再进入 restore,避免历史初始化临时服务竞态。来源提交动态冻结迁移名和 SQL 摘要;附件仍复用既有五类引用校验,base64 换行在 SQL 中去除。实际失败输出阶段、退出码和白名单错误类别,不回传原始 stderr。清理先校验本轮标签,身份不匹配不得删除;源归档前后摘要和监控/公网健康另行只读核验。
下文八段 RTO、历史 runner 部署/授权装置保留用于追溯,不作为本轮执行前提。本轮报告总恢复耗时、RPO、阶段和清理结果;应用与其他附件模型的端到端恢复不冒充已覆盖。
## 设计结论 ## 设计结论
采用“ARM 最新正式日归档 + 本轮私有临时副本 + 无宿主端口的一次性 PostgreSQL 16 + 空附件目录”的数据恢复方案。腾讯云生产只做演练前后公网 `/ready` 读取,不执行任何远端生产写操作;开发机不接收真实备份。 采用“ARM 最新正式日归档 + 本轮私有临时副本 + 无宿主端口的一次性 PostgreSQL 16 + 空附件目录”的数据恢复方案。腾讯云生产只做演练前后公网 `/ready` 读取,不执行任何远端生产写操作;开发机不接收真实备份。
@@ -143,3 +153,36 @@ Attempt 4 在 `dump_list=PASS/0` 后、实际 restore apply 前以 `restore_targ
## 证据边界 ## 证据边界
执行证据写入当前 task 的 `research/restore-drill-evidence.md`,随后只在现有 OPS7 Runbook 或人工试运行报告中补一条脱敏状态摘要;不新建 `docs/` 平行文档。不得保存主机地址、SSH 别名、完整远端路径、Secret、连接串、真实账号、表内容、附件名或原始日志。 执行证据写入当前 task 的 `research/restore-drill-evidence.md`,随后只在现有 OPS7 Runbook 或人工试运行报告中补一条脱敏状态摘要;不新建 `docs/` 平行文档。不得保存主机地址、SSH 别名、完整远端路径、Secret、连接串、真实账号、表内容、附件名或原始日志。
## 2026-09-09 最小人工恢复路径
### 与旧装置的关系
本阶段直接调用已跟踪的恢复脚本,不新增执行 runner、安装器、确定性渲染、sudoers 规则或 RTO 分段平台。现有 shadow/目标名称确认是恢复脚本内置安全条件,不删除、不改参数绕过。原正式演练指标继续保留未验,本阶段不改写五次历史结果。
本地已固定脚本身份:
| 文件 | SHA-256 |
| --- | --- |
| `scripts/restore-db.sh` | `3707d81db02364c5a4917f230ddde721731c04aa66ab746bd23a7bdcd475b95d` |
| `scripts/restore-storage.sh` | `de9e9340e16383d05b9821cdc73b65a0c28fbb2784a5ecb93d70c2e456ca651b` |
| `scripts/qa/validate-offhost-shadow-restore-attachments.py` | `2c2981d0e3f522145ffa1770f5ff519b3ffdfea74267e4d8ea9e30c130f7f0c2` |
### 手工操作清单(实际执行另批)
1. 在 ARM 获准管理员会话确认权限、镜像可用性、内存与磁盘余量。读取当时最新正式批次,而非复用 09-08 的摘要;核对 manifest 身份、PG/来源版本、DB/附件配对、字节数/摘要和24小时年龄。批次有歧义、超龄或损坏停止;现阶段没有选定备份,不声称上述前置已通过。
2. 生成本轮唯一标识,登记本轮容器与临时卷/目录;容器无宿主端口、network=none、明确 shadow 库名,使用临时凭据且不回显。只读使用归档数据或通过 stdin 输入,不挂生产数据卷、不向容器注入生产数据库连接,不启动 OA/worker,也不执行 migration/seed。
3. 容器内 TCP 回环查询 current_database,名称必须匹配本轮影子库;采用已有稳定就绪合同的连续两次成功和有界等待,不能以首次 pg_isready/socket 就绪直接开始。这里只是手工操作的就绪确认,不新增等待框架。失败保留阶段与安全错误后停止。
4. 明确指定 DB_TOOL_MODE=docker、DB_CONTAINER、POSTGRES_DB/USER、临时密码、RESTORE_SCOPE=shadow、RESTORE_CONFIRM_DATABASE,调用上述版本的 restore-db.sh。参数从本轮受控会话生成,不从生产 env 复制;私有原始输出仅留受控临时位置。脚本任何非零不忽略、不自动重试,是否源备份损坏需进一步依据,不能由“恢复失败”一句话判定。
5. 确认恢复整个命令退出 0;查询来源 migration 集合、失败状态以及原任务规定的12类关键表可读与数量摘要,不输出业务行。数量不是与实时生产一致的证明;恢复成功结论限于该具体批次。不要为了核对计数新增生产表数据读取。
6. 数据库通过后可按另获准的范围继续以 restore-storage.sh 恢复配对附件到本轮空目录,复用既有附件关联校验器;检查归档路径安全、文件集合和适用引用/摘要。若只批准数据库,附件明确 NOT_TESTED,不顺手解包。
7. 清理前生成短脱敏结果:批次指纹、开始/结束、退出码、DB/附件/应用验证状态、失败阶段和安全错误类别。原始敏感日志按既有受控清理规则处理,不能先删掉唯一诊断再留下“原因未知”。
8. 按本轮登记身份精确删除临时容器、临时卷/目录和临时凭据并回查;不删正式备份、不清理旧回滚镜像、不变更监控/timer。目标不明确立即停止清理并报告,不宽泛 prune。未清理完的结果不得写成完全闭环。
### 结果字典
- DB_RESTORE_PASS / STORAGE_NOT_TESTED / APP_NOT_TESTED:本批次数据库恢复成功,附件和应用仍未验;不是原正式演练整体 PASS。
- DB_RESTORE_PASS / STORAGE_RESTORE_PASS / APP_NOT_TESTED:成对数据恢复及既定一致性检查成功,不证明应用启动、正式切换或完整恢复耗时。
- STOPPED + 实际失败阶段 + 安全错误类别:不自动认定备份损坏;不继续业务发布来掩盖失败。若备份完整性或真正恢复数据步骤报错,优先单独报告并保全恢复点,不将其降为工具债。
- CLEANUP_PENDING:保留本轮目标标识与待清对象,不声明环境已恢复原状。
@@ -26,6 +26,193 @@
摘要匹配不等于实测恢复;旧镜像不撤销留痕删除或票据释放;若未来恢复生产,备份时点后数据丢失风险仍须另批。当前只准备清单,未 SSH、未复制或恢复真实备份。 摘要匹配不等于实测恢复;旧镜像不撤销留痕删除或票据释放;若未来恢复生产,备份时点后数据丢失风险仍须另批。当前只准备清单,未 SSH、未复制或恢复真实备份。
## 2026-09-11 已完成历史缺失标记:保留归档记录、暂不追查
用户:"可能是测试时候的……不在当前报销单里,影响不大的情况下,可以删除或标记就行。"选择保留原记录并在本任务/恢复缺口材料标记,不硬删、不伪造原件或改变存储路径。现有 invoice-pool PATCH 只允许 available/ocr_failed;archived 不允许编辑,因此不绕过接口状态守卫直接修改备注。此处置是任务/运维记录标记,不声称已新增系统页面标签或写入发票字段。
- [x] 限定单条只读回核:记录/路径摘要一致,仍 archived/manual,当前原件缺失、报销/明细/差旅及同路径报销/采购/回单/请假附件关联均不存在。UTC 2026-09-11 06:22 实测,只返回 1 条、7 个关联布尔值均 false,生产版本 fe025e65、运行身份不变。
- [x] 在当前 task 与 provenance/historical-missing-disposition.json 登记"已知历史附件缺失,低当前业务影响,保留归档记录、暂不追查";不再作为继续业务开发/其他验收的阻塞项。
- [x] 保留技术结论:该固定备份 DB 恢复通过、附件引用缺 1、app 未测试。标记不是恢复成功或发布例外批准,不修改恢复门禁或历史备份,不删除数据。未来关联/重要性证据变化时重新打开该缺口。
本阶段处置闭合:选择标记而非删除,不再反复恢复、扩大搜索或索取同一处置授权。标记仅在现有任务和恢复证据里,未给生产票据加 UI 标签、未修改其备注/状态,也未新增审计或业务记录;不能对外称"生产数据已打标"。回核代码语法与 5 个本地合成条件通过(无关联、已关联、文件重新存在、状态变化和投影未知);原始回执在 provenance/disposition-precheck-result.json。这里接受的是已知历史缺口的低当前业务影响,不是"已确认测试数据"或"完整恢复通过",也不替代候选独立复核和发布审批。
## 2026-09-11 单份 PDF 摘要比对完成:大小和摘要均未命中
用户对"只将此前提供的那份发票 PDF 与登记文件摘要比对,需要读取字节、不展示内容、不写回、不扩大搜索"回复"是"。复用本 task 与原证据 provenance 子目录,不新建任务。
- [x] 只读取指定本地 PDF;拒绝符号链接/非普通文件、超限及读取期间变化,原始摘要仅在内存中传给既有受控只读生产通道,不展示或写入证据。本地读取字节是本次明确授权范围,不能把整次操作称为"未读文件内容";远端没有读取发票文件。
- [x] 精确定位既有记录/路径双摘要,在有超时的 READ ONLY 事务内比较登记摘要和大小;仅返回布尔结果、范围与运行身份未变。
- [x] 4/4 合成测试验证大小/摘要四种组合、目标变化和失败回滚、非法输入不连接、SQL 投影边界;实际生产只读查询退出 0,结果未匹配。按约定停止,不扫描其他 PDF。
结果:候选文件 56436 字节,PDF 文件头有效且读取前后身份/大小/修改时间未变;目标此前登记 57786 字节,本次数据库直接比较 sizeMatches=false、sha256Matches=false、exactFileMatch=false。既有记录/路径及 archived/manual 标记核对一致,生产运行镜像/容器身份不变。文件内容及实际文件摘要均未展示,原件未上传生产,未改库、补文件、触发备份、恢复或部署。
该结果只证明候选文件与登记原件字节不一致,不证明两份 PDF 的业务内容必然不同;本轮未做票据解析或内容比较。不能以"相似文件"替换登记原件,也不删除/忽略缺失引用。此次限定比对已经结束;保留历史缺失和整体恢复 NO_GO,后续只能基于新的明确原件线索或单独批准的缺失处置方案推进,不再原样恢复或逐次扩大目录搜索。运行时候选和发布授权均未变化。
证据:provenance/single-candidate-result.json。测试命令 node --test artifacts/qa-flow/20260911-invoice-reference-readonly/provenance/compare.test.cjs(4/4);沿用已验证的有界生产读取封装,不改此前冻结脚本。新比对代码只属本地诊断材料,不进入运行时候选。
## 2026-09-11 单条来源元数据核对完成:历史 mock OCR、手工归档标记
用户回复"是,同意"。复用当前 task 与原证据目录的 provenance 子目录;只查该记录,不扩展到个人访问/登录历史、全量审计或其他票据。
- [x] 对照当前生产版本的 schema、上传与归档写入路径,确定来源字段能证明什么;988 个本地受版本控制的源码文件做静态字符串路径摘要对照,精确命中 0,不覆盖动态生成路径或未跟踪材料。
- [x] 有超时的 RepeatableRead READ ONLY 单条查询,按既有记录/规范化路径双 SHA-256 核对身份,只输出安全枚举、时间、技术元数据和身份关系布尔值;5/5 本地测试通过,含一次性真实 PostgreSQL 精确行筛选、成功/身份不符后的数据不变与清理;Python 语法检查通过。
- [x] 既有生产 SSH 通道执行成功,恰 1 条记录;SQL 定位摘要和 Node 规范化路径摘要一致,生产仍 fe025e65、镜像/容器启动身份前后不变。未读单张文件、未访问 OCR 服务、ARM 或历史数据库,未部署或写库。
- [x] 回填结果和证据边界,不自动补原件、改路径、删除或排除该条记录。
当前行实测:UTC 2026-07-27 12:07:48 创建(北京时间 20:07),12:16:37 归档(约 8 分 48 秒后);status=archived、archived_reason=manual,而不是 unused_timeout。创建/上传/归属对应同一账号,当前更新人也是该账号;不读取账号身份,不将当前 updated_by 等同于已查明的归档操作者。技术元数据为 PDF、57786 字节、日期/UUID 上传路径形态,存储摘要格式合法;ocr_provider=mock、VAT、尝试 1 次,存在请求/完成标记及结果引用,但未读取 OCR 结果内容。当前报销/明细/差旅关联仍均为空。
证据解释:这是一条 7 月的历史记录,带 mock OCR 和手工归档标记;不是自动超时归档标记,不是现在腾讯 OCR 配置的核验结果。路径形态与标准上传生成方式一致,但数据库没有可靠的"真实上传/测试来源"专门字段;mock OCR 可以处理真实 PDF,不能据此确认为测试数据。988 个源码静态路径无精确命中也不能排除动态夹具。当前及本地历史手工归档实现只更新状态/元数据,不删文件;未证明该历史提交就是当时部署版本,不据此声称已查明删除原因。
本轮已完成获准来源元数据核对,不再追加同路径恢复或扩大个人历史查询。缺失原因、原件内容、创建入口和测试身份仍未获确证,整体恢复继续 NO_GO(DB 已通过、附件引用未通过、app NOT_TESTED)。处置应保留该历史缺口:若后续寻找原件,建议先将用户此前明确提供的那一份发票 PDF 与该记录保存的文件摘要做定向比对,不扩大到整个 input 目录;这需要读取候选文件字节计算摘要,不能伪称纯元数据检查,须另批这个单文件范围。匹配只能证明候选文件与登记摘要一致,补回仍另批;不匹配则如实收口,不自动全盘搜索。补文件、改引用或经核实后的清理同样另批;不让员工代做恢复验证。
安全回执与测试汇总位于 artifacts/qa-flow/20260911-invoice-reference-readonly/provenance/production-result.json、source-and-result-summary.json、selftest-result.json。生产只返回脱敏定位摘要、时间、枚举、技术值和关系布尔值,未读取票据正文/金额/备注/银行字段、人员账号、访问日志或审计 payload。没有修改运行代码、提交或推送。
收尾 task validate、Trellis 治理、流程基线及 git diff --check 通过;本地脚本摘要与实际执行输入一致。敏感扫描仅命中合成测试的随机 credential 变量引用,主会话核为误报,保留原始 FAIL 与说明于 provenance/completion-checks.json;不修改扫描规则,不冒充独立复核。此轮没有新的规约变化,复用既有只读最小投影合同。
## 2026-09-11 历史备份目录项定位完成:限定范围内未命中
用户批准下一步限定只读查找,复用当前 task 和证据目录的 history 子目录,不改变运行候选或部署授权。
- [x] 仅从既有生产自动备份合同与 ARM 接收器合同定位正式历史备份根;不遍历全盘、inbox 或临时事务目录,不读取数据库包。
- [x] 本地合成用例验证路径规范化、目录项匹配、危险条目拒绝、读权限/损坏/变化独立报告,禁止 extractfile/extractall;设置批次数、归档大小、条目数和总耗时上限。python3 -B -I artifacts/qa-flow/20260911-invoice-reference-readonly/history/selftest.py:12/12 通过。
- [x] 完成两端获准正式历史目录项扫描。生产本地 7 份、ARM 日归档 7 份及周检查点 4 份均未匹配该路径;ARM 回执经既有 SSH 直接读取,冻结脚本摘要再次一致。ARM 日根另有 1 个非规范目录项未纳入,类型/内容未验证,不宣称全盘或所有历史备份都已检查。
- [x] 保留已扫描范围、失败/跳过原因及当前恢复仍 NO_GO 的结论;找到仅标待验证原件,补回或改引用另批。没有新增部署、备份、数据库或业务写入。
生产只读结果:正式本地保留批次 7/7 扫描完成,覆盖 UTC 2026-09-05 18:30 至 2026-09-10 18:30,路径匹配 0;非规范目录项 0,归档元数据及运行容器身份前后不变。该结果只排除这 7 份归档中的同一路径,不排除其他路径、较早备份或原始上传文件,不能推出永久丢失。没有核验单张文件内容或历史数据库身份,不把元数据稳定当作全包完整性通过。
执行前 ARM 普通身份目录访问为 PERMISSION_DENIED,sudo -n 需交互密码;管理员现已在自己的终端执行固定摘要脚本,权限前置已解决,不再要求重复输入或重新扫描。脚本最多每根 64 个批次、单归档 512 MiB、展开流 2 GiB/20000 条目、总扫描期限 120 秒并设 150 秒信号上限;只遍历目录项、不调用 extractfile/extract/extractall,不恢复或写业务数据。
ARM 实际结果 SEARCH_COMPLETE,UTC 2026-09-11 05:45 回执:日归档 7/7(备份时点 UTC 09-04 至 09-10),周检查点 4/4(最早 UTC 08-17);路径匹配 0,已扫归档元数据前后不变。11 个 ARM 目录对应 10 个不同 manifest,周/日存在重复来源,不计作 11 个独立恢复点。另 1 个非规范目录项只计数排除,未查类型或内容;未读历史数据库,不能由早期检查点无该路径推断当时已有这条记录或原件永久丢失。
本轮证据为 history/production-summary.json(远端 stdout 的脱敏汇总,非原始逐批回执)、arm-confirmed-result.json(SSH 回读实际完整安全回执)、completed-receipt.json;执行前权限受限回执、暂存及合成测试证据继续保留。SSH 回读不冒充外部独立复核,不修改此前阶段回执。限定路径搜索完成,但缺口未修复:数据库恢复 PASS、附件引用失败、应用 NOT_TESTED,整体仍 NO_GO。
下一步建议结束同路径重复扫描,围绕这一条记录确认来源与预期原件,再决定补原件、修路径或经核实后的历史缺失处置;进一步数据读取或任何修复须明确最小范围,不以"已归档、未关联"自动认定测试数据或忽略失败。不让员工代做恢复测试,不重复要求管理员运行同一命令,当前不部署、不提交推送。
## 2026-09-11 生产侧只读核对完成:源端同样缺少已归档发票文件
沿已授权生产通道完成最小字段 RepeatableRead READ ONLY 查询和应用实际存储根的 realpath/stat 检查;未打开单张发票内容,未读取金额、银行资料、人员信息或访问日志。6 个本地测试通过,其中一次性真实 PostgreSQL 验证了实际 SQL、关联投影、只读事务与记录不变;另 4 个合成归档目录项用例通过,禁止 extractfile。生产脚本退出 0,无需用户再次操作 ARM。
实测当前发票引用 42:源文件存在 41、缺少 1,无权限拒绝。缺少的引用同时不在已固定 manifest 对应的 107 个附件归档文件路径中。该记录 status=archived、archived_at 非空,创建和更新时点均早于备份;当前没有报销、明细、差旅关联,也不存在同路径的报销附件、采购附件、付款回单或请假附件引用。只保留记录/路径 SHA-256 脱敏定位,不回传原始 ID、路径或账号。
生产仍 fe025e65;容器启动时间/镜像未变,源端同一附件归档前后摘要匹配。没有生产业务写入、恢复、清理、下载真实备份或部署。该证据表明缺口在当前源端也存在,不是仅恢复副本缺文件;但本轮没有再次读取备份数据库的逐条身份,不能只凭当前数量和时间戳相同就声明已证明历史缺失原因。原件是否位于其他路径或更早备份、该记录是否来自测试以及何时/为何缺失仍未验证。
源码核对:当前 invoice-pool 手工归档路由和 invoiceArchiveJob 只更新状态/归档元数据,不删除文件;业务规约要求保留 PDF 原件。因此不把"已归档、未关联"自动视为可删或可排除条件,不改变 AC4 和整体 NO_GO。它当前没有查到报销/回单关联,不能扩大成整个系统所有业务已验收。
下一步建议围绕这一个脱敏引用查找可用原件或历史备份,并另行形成补文件/修引用或缺失处置方案。任何真实文件补回、引用修正、测试数据清理或发布例外继续另批;不再原样重跑恢复。就本轮授权而言,只读核对已经完成,数据缺口未修复,未做文件内容或历史备份搜索。
证据复用 artifacts/qa-flow/20260911-invoice-reference-readonly/:production-result.json、probe-inputs.json、selftest.log、final-receipt.json 与 evidence-manifest.json;脚本及回执均不进入运行时候选。收尾 task validate、治理、流程基线、git diff --check 通过,文档登记检查错误 0(保留既有警告)。定向敏感扫描命中本地合成测试的随机密码变量引用 1 处,主会话核为非凭据字面量的误报,保留原始 FAIL 与复核说明,不修改扫描规则,不冒充独立复核;未提交或推送。
## 2026-09-11 已授权生产引用与源文件只读核对
用户对限定只读核对回复"是"。本轮可连接当前生产,只查询发票引用定位、状态及关联标记,并用文件元数据判断源文件是否存在;不读取发票正文、金额、银行资料、完整人员信息或访问历史,不登录业务账号,不修复或删除记录。复用原 task,不新增任务。
执行顺序:核对当前生产容器及同一备份 manifest 身份;在有超时的 RepeatableRead READ ONLY 事务中取得最小引用投影;在实际应用存储根下做路径安全和 stat 检查;必要时只遍历同一附件归档的目录项,对照规范化路径摘要,不解包落盘或读取单张附件正文。缺文件、权限拒绝、路径异常必须分开;只输出数量、状态和脱敏引用标识。生产当前引用和备份时点的数据库引用不得因数量相同就冒称完全相同;历史来源因果不明时明确保留缺口。前后回读运行身份不变;不部署、不恢复生产、不清理测试数据。
## 2026-09-11 第三次诊断结果:发票池 1 条引用缺文件
已从 ARM 的 result.json 读取管理员实际执行回执,诊断不是仅由聊天转述:NO_GO / verify_attachment_references / ATTACHMENT_REFERENCE_MISMATCH。数据库再次 DB_RESTORE_PASS,68 条迁移集合和摘要匹配、异常 0,12 类关键表可读。当前同源批次年龄约 10.57 小时,manifest/包完整性通过。
- 附件解包成功,归档普通文件 107、解包普通文件 107,文件数一致。
- 发票池引用 42:41 条通过存在性、大小和 SHA-256,1 条 FILE_MISSING;该缺失条目未进行大小/摘要验证。
- 报销附件引用 5/5、付款回单引用 1/1 均通过存在性、大小和 SHA-256;采购/请假引用均为 0。
- 合计引用 48、通过 47、缺失 1、外部存储 0;诊断计数与原校验器一致,未跳过任何记录。
- 容器、卷、临时文件已清理,sourceArchiveUnchanged=true,生产前后公网健康与 ARM timer 状态未变;未部署或修改生产业务数据。
结论仅为"这份已校验备份恢复后,有 1 条发票池引用的目标路径不存在",不等于整包损坏,也不能推出原始票据永久丢失或该条一定属于测试数据。107 个归档文件是否在其他路径含有同一内容、生产源端是否仍存在目标文件、该引用是否关联真实报销均未验证。AC4 继续未通过,整个配对恢复仍 NO_GO,应用 NOT_TESTED;保留已有 DB 恢复通过证据。
接下来不原样重复恢复、不删除或排除这条记录。建议增加受控生产侧只读核对,与同一备份对照:只取发票引用定位所需元数据/关联状态和源文件存在性,区分源端缺失、备份遗漏、路径变化或历史异常;不读金额、票据正文、银行资料或个人访问历史。此生产附件/引用核对超出原"生产仅健康和备份元数据"的范围,待用户明确同意再执行;任何补文件、改引用、清理测试记录仍须另批写入。原任务继续承接,不新建 task,不声称原因已修复。
安全回执为 artifacts/qa-flow/20260911-paired-restore/arm-attempt3-result.json;此前两次结果和冻结脚本保留。当前没有遗留影子库可供继续查询,后续若需查看同批次数据库引用,须明确临时隔离读取和清理边界,不以此为由改查完整生产业务数据。
## 2026-09-11 配对恢复第二次:DB 通过,附件引用未通过
管理员回执由普通 SSH 独立读取:NO_GO / verify_attachment_references / ATTACHMENT_REFERENCE_MISMATCH。DB restore 退出 0,68 条迁移集合和 SQL 摘要一致、异常 0,12 类表可读;数据库恢复能力在当前 fe025e65 备份上已得到实证。ARM 最新同一 manifest 完整性通过、年龄约 9.97 小时、余量约 89.41 GiB;生产前后公网健康与 ARM timer 未变,源归档摘要未变,本轮容器/卷/临时数据已清理。
storage=NOT_TESTED 不是"未解包":执行到该阶段意味着 restore-storage.sh 已退出 0、归档和解包普通文件数已经一致;但回执未在最终引用门禁前写入这些结果。旧校验器只返回总数/不一致数/s3 数,本轮包装器又在写入计数前抛错,所以现有证据不能判断缺文件、大小不符、摘要不符、路径/行格式或外部存储是哪一类。不得称为备份损坏,也不得称为整个配对恢复通过;未启动 OA 应用。
接续仅补诊断,不修复或排除任何数据:按原五类影子库引用逐表运行原校验器,另计算白名单失败类别计数并与原结果交叉核对;所有表检查完再保持原失败门禁。解包/文件数、引用总数、适用大小/摘要验证与失败类别在清理之前写入脱敏结果,不输出账号、单据 ID、路径、内容或原始校验行。不读生产附件字段,不修改生产、备份或已恢复数据,不把真实备份下载到开发机。旧脚本与摘要保存在 attempt2/,真实失败回执为 arm-attempt2-result.json。原隔离资源已按约清理,若要定位现场原因须执行带诊断的同范围隔离过程,不能在没有现场数据时猜测原因或宣称已经修复。
诊断版准备完成:python3 -I selftest.py 20/20 通过,含真实 PostgreSQL 合成配对正例、迁移失败/附件缺失后的完整清理、12 种合成附件条件与原校验器逐一对照、重复引用不去重。缺失反例完整报告五类来源而非遇第一处即停止,解包成功和失败计数在清理后仍保留。本地本轮容器/卷残留 0;ARM 普通账号实测同一公网检查与两个 timer 通过。源端同一 manifest 再次完整性通过(约 10.32 小时),未触发新备份。
诊断脚本已普通账号暂存并由另一 SSH 回读摘要匹配,一次 sudo -n 尝试仍要求管理员密码,诊断版未实际恢复。待管理员执行新摘要命令后读取 attachmentChecks 的分类计数;不承诺下一次会 PASS,不将原附件引用失败降级忽略。当前候选、生产版本和备份不变,未部署;任务整体仍未完成。
## 2026-09-11 配对恢复首次执行暂停:校验器口径错误
ARM 管理员回执已由普通 SSH 读取确认:PAUSED / preconditions / PRODUCTION_CHANGED,restoreStarted=false;没有开始归档校验、复制、建容器或恢复,三个 Removed=true 表示没有本轮恢复资源需要清理,并非已经恢复后清理成功。
只读定位发现 server/src/config.js 的 normalizeBuildSha 将公网 buildSha 截成 12 位,而本轮校验器错误地直接与完整 40 位比较。当前公网 200/ok、版本前缀 fe025e65cfb5;生产容器完整版本和镜像再次核对未变,同一源端备份完整性仍通过(本次约 9.23 小时)。不能将该错误码解释为真实生产部署或备份损坏。
修订保持原任务范围:公网值严格等于已固定完整提交的前 12 位,不接受任意长度前缀;manifest/build/image 仍完整匹配。用实际 getConfig 生成测试值,补错误前缀、全长误用、过短/空值、非法期望值和失败零资源检查,并先在 ARM 普通身份实测公网检查再交付重试。旧脚本、摘要、测试和指令保存在同一证据目录 attempt1/,实际失败回执单独保留;不覆盖旧失败,不自动部署,不扩大 sudo 权限。
修订验证:python3 -I selftest.py 完整复跑 18/18 通过,本地本轮容器/卷残留 0;ARM 普通账号调用同一 production_ready 后实际通过,两个监控 timer 也通过。首次新增实际配置测试遇到本地 Node 不在执行器受限 PATH 的测试环境问题,测试改为解析当前 Node 绝对可执行位置后复跑通过,未改变远端执行器 PATH。不能再用"把输入完整 SHA 原样回显"的 stub 声称验证了生产健康接口。
修订脚本新目录暂存并由另一 SSH 回读摘要匹配;一次 sudo -n 受控执行尝试仍要求交互密码,未开始恢复。现在仅待管理员运行修订后的固定摘要命令;旧命令对应已证实错误的检查器,不再使用。新接续回执见 arm-unprivileged-precheck.json、arm-revision2-execution.json;schema、生产配置、备份和运行候选均未修改,独立候选复核仍待外部。
## 2026-09-11 接续:一次性 DB+附件配对恢复
用户"执行下一步工作"承接已推荐的 ARM 隔离配对恢复,不包含生产恢复、部署、真实业务操作或权限扩张。官方 current 已切回本 task;候选 fedcaed1 不变,独立复核仍标为待外部,不以 inline 主会话自审替代。
- [x] 刷新 ARM 既有 SSH 管理员渠道:仍需交互 sudo 密码;不改 sudoers、不申请通用 root shell、不收聊天密码。
- [x] 复用已发布的 restore-db.sh、restore-storage.sh 和附件校验器,准备固定摘要的一次性脚本;仅普通账号暂存,管理员在自己终端输入一次密码执行完整限定流程。
- [x] 先用本地合成备份验证真实 PostgreSQL 恢复、附件配对、失败关闭和本轮资源清理;本地通过不算 ARM 恢复通过。
- [ ] ARM 执行前重验最新归档及固定来源 manifest、24 小时时效、来源 build/image、现存 PG16 镜像、磁盘预算与监控;变化则暂停,不自动选择旧批次或拉镜像。
- [ ] 同一 manifest 的 DB+附件恢复至随机专属容器/卷与空目录;无宿主端口、network=none、随机临时凭据,不执行 migrate/seed、不启动 OA 或通知/OCR/银行调用。
- [ ] 对照来源提交动态核验迁移集合/摘要/异常项、12 类表及适用附件引用/大小/摘要;数据库记录只供机器校验,输出仅统计与安全错误类别。
- [ ] 无论成败,只清理本轮创建资源并回读确认;正式归档前后摘要匹配,生产只读健康、ARM 监控状态单独核验。实际结果出来后补原 task 回执,不提前声明完成。
本轮简化执行优先于下方历史 runner 的安装、sudoers、三阶段诊断和八段 RTO 装置要求;不扩建这些装置。保留数据 RPO/恢复总耗时与失败阶段,应用恢复明确 NOT_TESTED。当前备份早于今日两条所属主体修正,不能证明含备份之后变化;生产数据恢复仍需另批。脚本及本地合成证据留在联合副本 artifacts/qa-flow/20260911-paired-restore/,不纳入运行时候选。
本地 python3 -I selftest.py:15/15 通过,真实 PostgreSQL 合成 DB+附件正例、迁移摘要不符和附件缺失两类恢复后失败都完成清理;另覆盖 manifest 固定、来源身份、损坏、超龄、危险 tar/重复项、错误标签拒删、脱敏诊断和公网健康响应合同。12 类表和 68 个来源 migration 指纹用于合成验证,不冒充正式备份的行数验证。运行期间未下载真实备份、未调用 OCR/银行、未部署。
候选已提交树的 32/32 文件摘要仍与原冻结包一致,independentReview=PENDING_EXTERNAL_INLINE。源端最新备份再次完整性匹配,核验时约 8.69 小时,仍早于今日回填;ARM 两个监控 timer 的实际名称、enabled/active 已只读核对。ARM 脚本 0600、暂存目录 0700,另一 SSH 回读摘要匹配;现在仅等管理员终端密码执行,不是授权不足或恢复已失败。恢复脚本、输入摘要、本地日志与暂存回执分别见同目录 bundle-manifest.json、selftest-result.json、arm-staging.json。新增脚本只属本地受控执行材料,不改变 fedcaed1 候选,不自行提交推送这些补记。
## 2026-09-11 当前应用镜像制品已固定(不是恢复通过)
用户单独批准保存当前生产镜像并校验。本轮 fe025e65 镜像已保存为独立文件,1,119,357,440 字节,配置摘要/14 层/归档摘要通过,另一次 SSH 回读一致;文件 0600、目录 0700,独立于 Docker 存储但仍在生产同机。服务未重启,未 docker load,生产剩余约 40.04 GiB;旧镜像/备份全部保留。
同次另一窄授权仅回填财务/yj 的所属主体,已两行更新及独立连接回读。今日 02:30 的数据库备份早于该修正,不含这些后续变化;没有启动新备份。镜像回退不撤销主数据修正,配对 DB/附件和应用恢复仍待单独授权及受控管理员渠道,不用制品摘要匹配冒充恢复成功。
定位与完整回执共用联合副本 artifacts/qa-flow/20260911-authorized-preparation/,没有复制真实备份到开发机或向聊天输出银行资料。此前"当前版本独立制品未找到"是此授权执行前事实,已由本节更新;ARM 归档当前受交互凭据限制的缺口仍存在。
## 2026-09-11 限定只读刷新回执(未恢复)
本轮获准只读预检,没有恢复或镜像持久化授权。生产仍 fe025e65,候选 fedcaed1 已推送;今日 02:30:49 同版本备份的 manifest/DB/附件包大小摘要匹配,核验约 7.27 小时,PASS_INTEGRITY_ONLY。生产余量 41.09 GiB;现有登记回滚目录没有当前生产版本镜像的独立制品,3 份其他版本回执保留。
源端成功回执及最新 watchdog 时效检查正常;ARM dead-man 今日 06:30 退出 0、timer active,但本次普通账号归档路径读取被拒,sudo 非交互访问要求管理员密码。没有绕权限,今日 ARM 归档文件及目录容量未独立核验,也没有恢复、复制真实备份或清理资源。此前管理员回传的一次 DB_RESTORE_PASS 仍有效,仅不替代本次 DB+附件+应用配对验证。
证据共用联合副本 artifacts/qa-flow/20260911-readonly/。接下来先给受控隔离目标的管理员执行渠道,再按明确批准做配对恢复;只读检查不自动授权 docker save/load、服务重启或生产数据回退。不扩复杂演练器、不索要聊天明文密码。
## 2026-09-09 最小恢复阶段执行记录
- 已完成本地准备:在独立 ops-preparation worktree 修订 PRD/design,直接复用 restore-db.sh、restore-storage.sh 和已有附件关联校验器,固定三文件摘要;没有修改任何恢复脚本、安装器或 runner。
- 未访问 ARM 或生产,未选择当日备份、未核对当日容量/新鲜度、未创建容器或恢复数据。不能用历史09-08完整性结果当作本次前置。
- 实际执行待明确授权,授权对象须区分"数据库最小恢复"与"配对附件恢复";未批准附件就只出 DB 结果。代码提交/推送、正式应用恢复及生产切换均不包含在本阶段。
- 本地验收:恢复 shell 语法、task validate、Trellis governance 和 diff 检查;这些不替代真实恢复成功。原整体 AC 保持原状;下一次远端成功/失败证据仍追加 research/restore-drill-evidence.md,不创建新证据体系。
| 代码 | 发布 | 业务/恢复验收 | 等谁 |
| --- | --- | --- | --- |
| 只修订本地操作方案,无恢复脚本改动 | 不适用,本轮不发布 | 正式备份最小恢复未执行 | 等用户单独批准受控 ARM 操作,执行时再核对最新批次与容量 |
## 2026-09-09 获批数据库最小恢复:访问前置停止
用户已明确批准仅数据库的ARM隔离恢复、结果检查及本轮精确清理;附件未授权。本次不再处于"等待恢复授权",而是"管理员通道不可用"。
北京时间2026-09-09 09:22:40执行既有SSH通道的最小检查:SSH退出0、当前UID=1000、sudo -n true未通过。只证明当前非交互通用提权不可用;未采集sudo原始报错,不把它进一步断言为具体密码/策略故障。没有尝试新账号、读取密码、修改权限/sudoers、利用Docker绕过文件权限,亦未反复试探其他提权方式。
结果STOP_ADMIN_CHANNEL_UNAVAILABLE;backupRead=false、containerCreated=false、restoreStarted=false。未选择最新批次、未核对当日备份完整性/年龄,不复用昨日结果冒充已验;无本轮远端临时文件、容器或卷需要清理。该结果不是新的pg_restore失败,也不说明正式备份不可恢复。
脱敏回执保存在原主工作区artifacts/qa-flow/project-roadmap-execution-20260909/restore-minimal/access-check.json。下一步使用用户已说明可用的ARM管理员交互会话执行最小操作;密码只在用户终端输入,不要求发到聊天,不以清理或诊断为名扩权。本地规划与清单仍保留,生产部署和真实付款继续暂停。
## 2026-09-09 最小数据库恢复阶段闭合
- [x] 用户既有管理员交互会话完成最新批次完整性检查;原版恢复脚本退出 0。
- [x] 管理员回传来源 migration 66 项精确匹配、异常 0,12 类关键表可读及数量摘要;本地来源/候选 migration 目录集合一致。
- [x] 管理员回传本轮容器、卷及临时文件已精确清理;不读取或保存业务明细与临时凭据。
- [ ] 配对附件恢复、引用验证、应用恢复及原整体演练其余验收:本次未执行,不以数据库阶段成功代替。
- [ ] 独立远端复核:未执行;本阶段以管理员执行回执为证,代理本地审查不称独立复核。
证据见 research/restore-drill-evidence.md 的同日最小恢复记录。旧访问停止段为历史前置,不再是本阶段当前阻塞;没有变更 task 的整体完成状态。未提交、未推送、未部署。后续规划仍先 seed 初始化/升级分离,不在本轮扩大恢复范围或自动发布。
## 执行前门禁 ## 执行前门禁
- [x] 确认任务已在用户审阅最终规划摘要后由 `task.py start` 进入 `in_progress`。 - [x] 确认任务已在用户审阅最终规划摘要后由 `task.py start` 进入 `in_progress`。
@@ -1,5 +1,38 @@
# 隔离备份恢复演练 # 隔离备份恢复演练
## 2026-09-09 最小验真阶段(本地准备已批准,远端执行另批)
用户批准综合规划开始本地实施。本 task 不再把安装器/固定 runner 的完善当作下一次数据库恢复验真的前置;新增受控管理员人工最小路径。只在既有授权通道操作,不能以“不用 sudoers”解释为绕过现有读取权限。未收到本次 ARM 真实备份恢复授权,本轮不连接远端。
本阶段数据库结果和配对附件结果分开:数据库退出 0、来源 migration 与关键表检查通过,才可记 DB_RESTORE_PASS;附件未执行记 STORAGE_NOT_TESTED;不启动应用,不宣称应用或完整灾备 RTO 已验证。原整体 Goal、数据库/附件 AC 和历史 NO_GO 保留;最小阶段通过不自动勾选原整体 AC。原 runner 渲染/安装及分段 RTO 不作为本次最小阶段新增工程前置,仅适用于保留的正式演练合同。
保留生产零写入/零重启、影子目标确认、无宿主端口、批次来源与24小时新鲜度、配对摘要、敏感输出限制、非零失败关闭及本轮精确清理。若本地脚本在实际最新批次上失败,保留脱敏诊断并停止;不得现场放宽条件、转用旧批次或自动重试。Attempt 3 底层原因仍未知,不追认所有失败均为工具问题。
## 2026-09-11 本轮执行口径
用户认可在当前无业务关联、影响较低的前提下删除或标记。选择非破坏性标记:复核仍为 archived/manual、原件缺失且无当前业务关联后,在现有任务/恢复缺口记录登记"已知历史附件缺失,保留记录,暂不继续追查"。当前票据接口不允许编辑 archived 记录,不为添加备注绕过状态守卫改库,不新增缺失状态或删除记录。"可能是测试数据"仍是推测,不写成已确认事实。此处置不把恢复 AC4 改为通过,不改校验器、不排除缺失项,也不自动授权部署。
该处置已完成:再次只读核对无当前报销/明细/差旅及同路径附件/回单关联,记录和源文件缺失状态未变;已在本 task 和恢复证据中标记,生产数据保持原样。按用户决定暂不追查,不阻塞其他业务开发/验收;技术恢复仍如实记录 1 条附件引用缺失,发布例外及独立复核不由该标记替代。
用户批准对此前明确提供的单份发票 PDF 读取字节计算 SHA-256,并与唯一缺失记录登记的文件摘要、大小比对。不展示文件内容或原始文件摘要,不遍历 input 目录、不读其他文件,不上传原件、不补回或改生产。只读返回匹配/不匹配;匹配不代表原件已补回、备份已更新或恢复已通过,不匹配则停止,不自动扩查。
本次单文件比对已执行:大小和 SHA-256 均不匹配,不能将它补作登记原件。已按约定停止搜索,未补文件、改库或部署;不推断 PDF 业务内容必然不同。当前仍保留单条历史附件缺失,恢复 AC4 未通过;后续需新的明确原件线索或另批缺失处置方案,不重复恢复或自动扩大搜索。
用户另行批准只读核对唯一缺失记录的创建/归档来源元数据。本轮只定位既有记录 SHA-256 与路径 SHA-256,查询该行的时间、状态、归档原因枚举、创建/上传/更新身份关系布尔值及必要技术元数据;不读取人员账号、个人访问历史、审计 payload、票据正文、金额、备注或银行字段,不改数据。结合本地已发布源码解释字段含义;没有确证不得归因为测试数据或手工删除。
此元数据核对已完成:记录创建于 2026-07-27,约 9 分钟后标记 manual 归档,PDF 技术类型,存有 mock OCR 处理记录;当前无报销/明细/差旅关联。没有读取 OCR 结果或文件内容;不因 mock 或手工归档标记认定为测试数据,也不推断是谁删除了文件。来源入口与缺失原因仍未证实,AC4 未通过;后续内容摘要比对必须单独明确读取范围,任何修复仍另批。
用户进一步批准围绕该唯一缺失引用,只读查历史备份目录项和文件元数据以寻找原件。仅在已有生产/ARM 管理渠道可读的正式备份目录内做有界搜索,按已记录的规范化路径 SHA-256 匹配;不读票据正文、不恢复数据库、不解包落盘、不补文件或改引用。找到目录项只算候选原件,内容一致性与可恢复性仍须另验;权限拒绝、覆盖范围不足和未命中必须分开,不以未找到推断永久丢失。
该限定搜索已完成:生产 7 份、ARM 日归档 7 份及周检查点 4 份均未命中;ARM 另 1 个非规范目录项未纳入,不能称为所有历史备份都已检查。管理员已执行且主会话 SSH 回读结果,不再等待该次密码或执行。缺失引用未修复,AC4 继续未通过,不扩大为永久丢失或测试数据结论。
第三次诊断已确认发票池 1 条引用缺文件。用户另行批准生产侧限定只读核对:最小引用/关联状态和源文件存在性,可对照已固定附件归档目录项;不读票据正文、金额或银行资料,不改数据或生产配置。此限定读取是下文历史"生产仅健康探测"范围的显式补充,不授权恢复生产或修复记录。
用户已同意继续做 ARM 一次性 DB+附件隔离配对恢复;不是生产恢复或发布授权。本轮复用已有 shadow 恢复脚本,以一次管理员终端操作完成前置验证、临时恢复、校验与清理,不再安装历史 runner、sudoers 入口或增加分段计时装置。按原 R1–R4 保留输入身份、时效、隔离、迁移和附件配对边界,R5 记录总耗时与安全失败阶段即可。实际配对恢复已执行:DB 通过、附件引用失败,清理完成;不以本地合成通过替代,也不原样重跑已知失败过程。
固定输入来自今日最新源端校验通过的 fe025e65 备份;若执行时 ARM 最新批次、manifest、来源 build/image 或现存 PostgreSQL 镜像变化,暂停刷新证据,不自动取旧批次或拉镜像。恢复不启动 OA 应用,app 必须标 NOT_TESTED;镜像回退和 DB/附件恢复仍是不同能力。09-09 管理员回传的最小 DB_RESTORE_PASS 已有效记录,不再沿用"从未成功恢复数据库"的旧结论。
## Goal ## Goal
在不覆盖、不暂停腾讯云生产 OA 的前提下,使用 ARM 备份机上最新的完整异机批次完成一次数据库与附件的隔离影子恢复,证明“备份存在”进一步达到“最新成对数据备份可恢复”,并记录实测数据恢复 RTO、RPO 和脱敏证据。 在不覆盖、不暂停腾讯云生产 OA 的前提下,使用 ARM 备份机上最新的完整异机批次完成一次数据库与附件的隔离影子恢复,证明“备份存在”进一步达到“最新成对数据备份可恢复”,并记录实测数据恢复 RTO、RPO 和脱敏证据。
@@ -61,12 +94,12 @@
## Acceptance Criteria ## Acceptance Criteria
- [ ] AC1:ARM 最新正式日批次通过文件集、manifest、字节数和 SHA-256 校验,来源构建是当前构建的祖先或相同,且实测 RPO 不超过 24 小时;是否已形成当前构建同源恢复点单独记录。 - [x] AC1:ARM 最新正式日批次通过文件集、manifest、字节数和 SHA-256 校验,来源构建是当前构建的祖先或相同,且实测 RPO 不超过 24 小时;是否已形成当前构建同源恢复点单独记录。2026-09-11 第二次配对执行已证同源 fe025e65、约 9.97 小时;不代表未来批次自动通过。
- [ ] AC2:腾讯云生产在演练前后 `/ready` 均正常,生产容器、数据库、附件、配置、timer 和业务数据零写入、零重启、零切换。 - [ ] AC2:腾讯云生产在演练前后 `/ready` 均正常,生产容器、数据库、附件、配置、timer 和业务数据零写入、零重启、零切换。
- [ ] AC3:一次性 PostgreSQL 16 影子库无宿主端口,数据库恢复成功;恢复 migration 集合与 manifest 来源提交一致且无失败/未知项,来源与当前 migration 差异已明确;固定 12 类关键表均可读并形成仅含数量的脱敏摘要。 - [x] AC3:一次性 PostgreSQL 16 影子库无宿主端口,数据库恢复成功;恢复 migration 集合与 manifest 来源提交一致且无失败/未知项,来源与当前 migration 差异已明确;固定 12 类关键表均可读并形成仅含数量的脱敏摘要。2026-09-11 第二次配对执行:68 条迁移集合/摘要匹配、异常 0、12 类表通过;附件失败不抹掉此项实证。
- [ ] AC4:附件安全恢复成功,归档与解包普通文件数一致;所有纳入范围的本地附件引用均安全且存在,适用的哈希/大小核对无不一致。 - [ ] AC4:附件安全恢复成功,归档与解包普通文件数一致;所有纳入范围的本地附件引用均安全且存在,适用的哈希/大小核对无不一致。
- [ ] AC5:记录本轮数据恢复 RTO、RPO、同源恢复点可用滞后、开始/结束时间、失败点或 finding、Go/Pause/No-Go 结论;即使恢复未开始也保留已观测空窗,不把任何指标表述为完整应用灾备 RTO 或 SLA。 - [ ] AC5:记录本轮数据恢复 RTO、RPO、同源恢复点可用滞后、开始/结束时间、失败点或 finding、Go/Pause/No-Go 结论;即使恢复未开始也保留已观测空窗,不把任何指标表述为完整应用灾备 RTO 或 SLA。
- [ ] AC6:演练结束后本轮容器、端口、网络和临时数据全部清理,ARM 正式归档与现有 dead-man/健康监控不受影响。 - [x] AC6:演练结束后本轮容器、端口、网络和临时数据全部清理,ARM 正式归档与现有 dead-man/健康监控不受影响。2026-09-11 第二次配对执行已清理,源归档摘要与 timer 未变;后续新运行须重新核验。
- [ ] AC7:任务校验、Trellis 治理、流程合同、文档审计、敏感扫描和 `git diff --check` 通过;证据不含敏感信息。 - [ ] AC7:任务校验、Trellis 治理、流程合同、文档审计、敏感扫描和 `git diff --check` 通过;证据不含敏感信息。
## Out of Scope ## Out of Scope
@@ -79,7 +112,7 @@
## Blocking Open Questions ## Blocking Open Questions
- 无。构建门禁已裁决为“来源提交是当前构建祖先或相同 + 批次年龄不超过 24 小时”;实施仍需用户审阅本次修订后的最终规划摘要并再次明确批准。 - 单条历史缺失已按用户决定完成非破坏性标记,来源未证实但暂不继续追查;不再等待其处置授权或管理员密码。实际附件恢复仍未全部通过,不自动改为完整恢复成功。若未来需要修复或生产恢复/部署,仍需对应明确范围与授权。
## Test Plan ## Test Plan
@@ -87,3 +120,10 @@
- 通过现有 shadow 恢复脚本完成 PostgreSQL 16 和附件恢复,验证来源提交的 migration 集合、与当前构建的 migration 差异、固定 12 类表及附件引用聚合。 - 通过现有 shadow 恢复脚本完成 PostgreSQL 16 和附件恢复,验证来源提交的 migration 集合、与当前构建的 migration 差异、固定 12 类表及附件引用聚合。
- 复核无宿主端口、生产前后 `/ready`、ARM 监控未受影响和本轮临时对象零残留。 - 复核无宿主端口、生产前后 `/ready`、ARM 监控未受影响和本轮临时对象零残留。
- 运行 task validate、Trellis governance、process contract、docs registry、敏感扫描和 `git diff --check`。 - 运行 task validate、Trellis governance、process contract、docs registry、敏感扫描和 `git diff --check`。
## 2026-09-09 数据库最小恢复执行授权
用户对“通过ARM既有合法管理员通道,核对当时最新正式备份,在一次性隔离PostgreSQL中执行数据库恢复、检查结果并清理本轮临时资源”明确回复“是”。本次只批准数据库,不解包附件、不启动OA、不触碰生产、不改权限或sudoers、不删除正式备份。权限不足或恢复失败停止并报告;不需再次征询同一范围的授权。
最初代理非交互管理员通道检查未通过;随后用户通过既有 root 交互会话执行获批操作。2026-09-09 管理员回传本批次 DB_RESTORE_PASS、66 项 migration 集合匹配、12 类关键表可读和本轮资源清理完成。该结果并非代理独立远端验证;附件与应用仍未验,整体任务保持未完成。详见 research/restore-drill-evidence.md 同日记录。
@@ -592,3 +592,37 @@ receiver 聚合结果:正式原子日归档共 7 个,最新归档在复核
调查器本身出现并安全关闭了两类本地缺陷:首次生产探针错误假设部署账号可 sudo,安装前即被权限拒绝;前两次 ARM 探针把 dead-man unit 写成近似但不存在的名称,均在状态读取前以合同不可读失败。每次 root 入口、sudoers candidate、普通用户暂存均为 0。最终版本从仓库 receiver 安装器机械提取并比较 unit basename,并将生产探针改为无提权只读执行;Bash/Python 语法、两种 ExecStart 解析 fixture 和敏感扫描通过。上述缺陷不改变生产、ARM、归档、timer 或告警状态,也没有伪造调查结果。 调查器本身出现并安全关闭了两类本地缺陷:首次生产探针错误假设部署账号可 sudo,安装前即被权限拒绝;前两次 ARM 探针把 dead-man unit 写成近似但不存在的名称,均在状态读取前以合同不可读失败。每次 root 入口、sudoers candidate、普通用户暂存均为 0。最终版本从仓库 receiver 安装器机械提取并比较 unit basename,并将生产探针改为无提权只读执行;Bash/Python 语法、两种 ExecStart 解析 fixture 和敏感扫描通过。上述缺陷不改变生产、ARM、归档、timer 或告警状态,也没有伪造调查结果。
当前独立生产 finding 为:最新可用异机恢复点已超过 24 小时目标,自动备份传输链正在失败;恢复演练因此保持阻塞。该事件不再作为恢复演练的普通 finding 排队处理,建议另建 Trellis 生产事件任务承载传输/容量调查与修复,当前演练任务仅保留证据交叉引用和阻塞关系。后续优先顺序是另行批准修复生产到 ARM 的传输链并形成新的原子批次,确认自动备份/两端 dead-man 与 watchdog 恢复,再对新批次重新执行身份探针。手工补跑备份、修改网络/SSH/timer、清理 partial 或发送测试告警均不在本次只读批准内。 当前独立生产 finding 为:最新可用异机恢复点已超过 24 小时目标,自动备份传输链正在失败;恢复演练因此保持阻塞。该事件不再作为恢复演练的普通 finding 排队处理,建议另建 Trellis 生产事件任务承载传输/容量调查与修复,当前演练任务仅保留证据交叉引用和阻塞关系。后续优先顺序是另行批准修复生产到 ARM 的传输链并形成新的原子批次,确认自动备份/两端 dead-man 与 watchdog 恢复,再对新批次重新执行身份探针。手工补跑备份、修改网络/SSH/timer、清理 partial 或发送测试告警均不在本次只读批准内。
## 2026-09-09 最小数据库隔离恢复通过(管理员执行回传)
证据来源:用户在 ARM 既有 root 交互会话执行本会话提供的分步命令,回传脱敏 JSON;代理未获得该会话的管理员凭据,未独立登录读取恢复库或核验远端清理。代理独立完成的是本地来源提交 migration 集合提取、原恢复脚本摘要核对及普通 SSH 临时上传。以下远端结果按管理员执行回执记录,不冒充独立复核。
- 批次创建时间:2026-09-08T18:30:05+00:00,即北京时间 2026-09-09 02:30:05;完整性回执年龄 7.35 小时,不是后续任意时点的当前年龄。
- manifest SHA-256:`968426af94092cd05a672817178e46773837d893fc9bd0491a4c76d3a49a611f`;来源构建 `f223bfb72fb2baef51199702c2def84c41af7aef`。
- 数据库包和配对附件压缩包均大小与 SHA-256 匹配。附件仅校验压缩制品,未解包、未做附件引用验证。
- 镜像为既存 `postgres:16-alpine`,linux/arm64;固定镜像 ID `sha256:c05eced0bdb41ea9b95a656472a6aa4d50cad0d8a2e33d14eb1c53fd6204f2ae`。先前只检查 `postgres:16` 的 false 不能解释为所需镜像缺失。本轮未下载镜像。
- 执行前回传 archive/docker 可用空间均 89.35 GiB;恢复命令另设磁盘及可用内存下限检查。此值不是生产容量或清理后容量。
- 使用原版 `restore-db.sh`,SHA-256 `3707d81db02364c5a4917f230ddde721731c04aa66ab746bd23a7bdcd475b95d`;一次性 PG16 容器无宿主端口、network=none、独立本轮卷。连续两次 TCP 回环确认影子库后执行,恢复脚本退出码 0,未自动重试。
- 验证只读事务使用 ON_ERROR_STOP;来源 migration 集合 66 项精确匹配、异常 migration 0。名称集合 SHA-256 为 `18967953c57e88cab87453074e7e2c5a566482b60da1f67276d342b976c99d35`。本地从来源 Git 提取,非旧证据中 65 项硬编码;与本地候选 `b918530` 的目录集合一致。未执行 migrate 或 seed。
| 关键表 | 恢复后行数 |
| --- | ---: |
| departments | 5 |
| employees | 8 |
| employee_roles | 14 |
| purchase_requests | 0 |
| reimbursement_forms | 6 |
| invoice_pool | 36 |
| payment_batches | 2 |
| contract_registry | 0 |
| projects | 0 |
| purchase_attachments | 0 |
| reimbursement_attachments | 5 |
| payment_batch_receipt_attachments | 1 |
最终回执:`DB_RESTORE_PASS`,12 类关键表全部可读;`storage=NOT_TESTED`、`app=NOT_TESTED`。行数只描述该具体备份恢复后的聚合结果,不是与实时生产逐项比对或业务流程可用的证明。
清理回执:`cleanupPending=false`、`containerRemoved=true`、`volumeRemoved=true`、`temporaryFilesRemoved=true`。检查通过后精确删除本轮容器、卷、临时凭据、日志及上传脚本;命令不删除正式归档、不清理旧回滚镜像、不改权限/sudoers或生产服务。本轮未独立查询生产健康或 ARM 监控,不能据此勾选原整体演练的前后健康验收。
结论限于本批次:已实际证明数据库恢复命令及约定数据库检查通过,不能继续笼统记为“本任务当前正式备份数据库恢复能力仍无答案”。历史五次 NO_GO 保留,Attempt 3 原因仍未知;本次成功不追溯解释历史失败,也不证明所有备份都可恢复。配对附件恢复、应用恢复/业务验收、完整灾备 RTO/RPO 和独立复核均未完成,整体 task 不关闭、不归档,不自动取得发布例外或部署资格。