加固备份恢复本地基线(07-26-internal-ops-recovery-baseline)
这个提交包含在:
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,61 @@
|
||||
# 内部 OA 运行与恢复底线设计
|
||||
|
||||
## 总体边界
|
||||
|
||||
本任务将现有四个底层脚本保留为单一职责工具,在其上增加一个成对备份入口和一个隔离演练入口。所有默认值面向本地/影子环境;真实环境只能沿 runbook 的人工门禁执行。
|
||||
|
||||
```text
|
||||
backup pair entry
|
||||
├─ backup-db.sh → <run-id>.db.dump
|
||||
├─ backup-storage.sh → <run-id>.storage.tar.gz
|
||||
└─ integrity checks → <run-id>.manifest
|
||||
|
||||
isolated drill
|
||||
├─ disposable PostgreSQL/database
|
||||
├─ disposable source/restore directories
|
||||
├─ synthetic marker + synthetic attachment
|
||||
└─ restore → compare → cleanup exact targets
|
||||
```
|
||||
|
||||
## 备份合同
|
||||
|
||||
- 一个批次只使用一个 `run_id`,由上层入口传递明确的 `OUT_FILE` 给现有底层脚本。
|
||||
- 先把产物写入批次临时目录;DB 和附件均成功并通过完整性检查后,再写 manifest 并把批次标记为完整。
|
||||
- manifest 使用稳定、脱敏的键:`schemaVersion`、`runId`、`createdAt`、`buildSha`(如可安全取得)、两份相对文件名、字节数和 SHA-256。
|
||||
- 不把连接串、容器密码、存储源路径、真实业务文件清单或主机绝对路径写入 manifest。
|
||||
- 本任务不负责异机传输实现;runbook 要求操作者把完整批次复制到异机/异盘,并记录保留周期。
|
||||
|
||||
## DB 恢复安全合同
|
||||
|
||||
- 恢复前通过数据库连接读取 `current_database()`,不得只相信 URL 字符串或调用者描述。
|
||||
- 调用者必须提供与实际数据库名一致的确认值,并显式选择 `shadow`。
|
||||
- 实际目标库名还必须包含独立的 `shadow`、`drill` 或 `restore_test` 标记,不能仅靠调用者把模式写成 `shadow`。
|
||||
- 确认缺失、连接失败、数据库名不匹配或模式不是 `shadow` 时,在 `pg_restore --clean` 之前退出。
|
||||
- Docker fallback 也必须执行同样的数据库名探测和确认,不得成为绕过路径。
|
||||
- 真实/生产恢复不在本任务脚本默认路径内;未来如确需开放,应另建任务定义双人确认和事故编号。
|
||||
|
||||
## 附件恢复安全合同
|
||||
|
||||
- 必须显式传入恢复目标,不再默认落入试运行数据目录。
|
||||
- 明确拒绝仓库的默认试运行附件目录,即使该目录碰巧为空。
|
||||
- 影子恢复目标必须为空;非空时退出,不执行覆盖或合并。
|
||||
- 解压前列出归档成员并拒绝绝对路径、空成员和包含 `..` 路径段的成员。
|
||||
- 只对已验证的归档执行解压;演练通过内容 SHA-256 比较证明往返一致。
|
||||
|
||||
## 隔离演练
|
||||
|
||||
- 数据库只使用一次性本地容器或明确命名的 disposable database,插入不含业务信息的 marker 表/行。
|
||||
- 附件只使用临时目录和合成文本/二进制样件。
|
||||
- 演练记录开始/结束时间、各阶段通过状态和相对产物名;不记录连接串、容器密码或临时绝对路径。
|
||||
- 清理只针对演练创建的明确容器、数据库和临时目录;不得使用宽泛路径或工作区根目录。
|
||||
|
||||
## 兼容与回滚
|
||||
|
||||
- `backup-db.sh` 和 `backup-storage.sh` 的现有单独调用继续可用;推荐运维入口改为成对备份命令。
|
||||
- `restore-db.sh`/`restore-storage.sh` 的安全收紧属于有意的命令合同变化,runbook 和 checklist 必须同步更新。
|
||||
- 回滚代码可恢复旧脚本,但已经生成的 dump/tar 仍保持 PostgreSQL/tar 标准格式,不被新 manifest 锁定。
|
||||
- 如演练环境缺少 PostgreSQL 客户端和 Docker,停止 DB 动态演练;仍可完成 shell 静态检查和附件演练,不得转向真实环境补测。
|
||||
|
||||
## 外部门禁
|
||||
|
||||
以下事项不因本地演练通过而自动获批:读取真实运行环境、复制真实备份、建立异机定时任务、停机、恢复真实库/附件、部署和公网 `/ready`。进入这些步骤前再次列出目标、只读/写入范围和回滚方式并取得明确批准。
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,35 @@
|
||||
# 内部 OA 运行与恢复底线执行计划
|
||||
|
||||
## 实施步骤
|
||||
|
||||
- [x] 审计现有备份/恢复脚本、部署卷、runbook 和 checklist,冻结现有兼容入口与风险点。
|
||||
- [x] 增加 DB + 附件成对备份入口、统一批次号、完整性检查、SHA-256 和脱敏 manifest。
|
||||
- [x] 收紧 DB 恢复:实际数据库名探测、影子模式和精确确认必须在破坏性命令前通过。
|
||||
- [x] 收紧附件恢复:显式目标、空目录门禁和 tar 路径安全检查。
|
||||
- [x] 增加隔离合成数据演练,覆盖成功往返及关键负向场景。
|
||||
- [x] 更新 runbook/checklist 和部署规约,区分普通备份、影子演练和真实事故恢复。
|
||||
- [x] 运行任务测试计划和 Trellis 治理门禁;按实际结果记录本地 RTO,不伪造 `/ready` 或异机证据。
|
||||
- [ ] 在单独授权后再执行真实运行来源、职责分离、异机副本和 `/ready` 验收。
|
||||
|
||||
## 推荐文件边界
|
||||
|
||||
- 复用并按需修改:`scripts/backup-db.sh`、`scripts/backup-storage.sh`、`scripts/restore-db.sh`、`scripts/restore-storage.sh`。
|
||||
- 最多新增两个小脚本:一个成对备份入口、一个 QA-only 隔离演练入口;不创建通用备份框架。
|
||||
- 更新现有 `deploy/trialrun/runbook.md`、`deploy/trialrun/checklist.md` 和 `.trellis/spec/hyb-oa/deployment-trialrun.md`,不新建常驻平行业务文档。
|
||||
|
||||
## 验证命令
|
||||
|
||||
- `bash -n scripts/backup-db.sh scripts/backup-storage.sh scripts/restore-db.sh scripts/restore-storage.sh scripts/backup-all.sh scripts/qa/run-backup-restore-drill.sh`
|
||||
- `bash scripts/qa/run-backup-restore-drill.sh`
|
||||
- `node deploy/trialrun/validate-env-contract.js`
|
||||
- `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`
|
||||
|
||||
## 回滚点
|
||||
|
||||
- 成对入口失败:保留四个底层脚本,不写完整 manifest。
|
||||
- DB/附件安全门禁与现有操作冲突:先修 runbook 和调用方,不放宽默认门禁。
|
||||
- 动态演练环境不可用:记录缺失依赖并停止,不使用真实数据替代。
|
||||
- 任何命令解析到生产、远程或含真实数据的目标:立即停止并请求明确授权。
|
||||
@@ -0,0 +1,77 @@
|
||||
# 内部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`,因此最后一项验收保持未完成。
|
||||
|
||||
## 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`.
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"id": "internal-ops-recovery-baseline",
|
||||
"name": "internal-ops-recovery-baseline",
|
||||
"title": "内部OA运行与恢复底线",
|
||||
"description": "为5至10人内部OA建立低维护、可验证、防误操作的数据库与附件成对备份恢复基线,并明确真实环境授权门。",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": "backup-restore-safety-local-drill-operational-baseline",
|
||||
"package": null,
|
||||
"priority": "P0",
|
||||
"creator": "shunli",
|
||||
"assignee": "shunli",
|
||||
"createdAt": "2026-07-26",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [
|
||||
"scripts/backup-db.sh",
|
||||
"scripts/backup-storage.sh",
|
||||
"scripts/backup-all.sh",
|
||||
"scripts/restore-db.sh",
|
||||
"scripts/restore-storage.sh",
|
||||
"scripts/qa/run-backup-restore-drill.sh",
|
||||
"deploy/trialrun/runbook.md",
|
||||
"deploy/trialrun/checklist.md",
|
||||
"server/README.md",
|
||||
".trellis/spec/hyb-oa/deployment-trialrun.md"
|
||||
],
|
||||
"notes": "本地阶段完成:成对备份、脱敏 manifest、影子库/空目录恢复门禁和合成数据演练已通过,最近本地参考 RTO 6 秒;真实运行来源、职责分离、异机副本和部署后 /ready 仍待单独授权。",
|
||||
"meta": {}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户