diff --git a/.trellis/spec/hyb-oa/deployment-trialrun.md b/.trellis/spec/hyb-oa/deployment-trialrun.md index 68618f25..d689b490 100644 --- a/.trellis/spec/hyb-oa/deployment-trialrun.md +++ b/.trellis/spec/hyb-oa/deployment-trialrun.md @@ -1135,6 +1135,12 @@ PGPASSWORD="$POSTGRES_PASSWORD" docker exec -e PGPASSWORD db pg_dump ... - `install` writes only the exact managed probe/helper/state/unit targets and leaves the timer disabled. `enable`, ARM writes, real robot messages and a controlled failure/recovery drill remain separately approved operations. +- Before any remote write, prove the selected SSH identity can run `sudo -n` + and that no application-health artifact already exists. A password prompt is + a stop condition, not permission to request or transmit a password in chat. + If the operator creates a temporary privilege rule, remove its exact file at + the end, validate sudoers in the same root invocation, invalidate the sudo + timestamp and prove the original non-interactive access is gone. - `uninstall` removes only the exact application-health units and probe. It retains the shared helper, webhook, state evidence and all backup components. @@ -1149,6 +1155,11 @@ PGPASSWORD="$POSTGRES_PASSWORD" docker exec -e PGPASSWORD db pg_dump ... - A first isolated failure followed by health -> no failure or recovery message. - Any overridden managed target or service name -> installer stops before `systemctl`, file replacement or removal. +- SSH succeeds but `sudo -n` requests a password -> stop before upload or + systemd mutation; resume only after an operator establishes a separately + approved, non-chat root path and rerun the complete preflight. +- Temporary privilege rule remains after final health verification -> rollout + is incomplete even when the timer is healthy. - ARM itself or its network/robot path unavailable -> the monitor can be silent; do not report this design as having an independent third-party dead-man. @@ -1169,6 +1180,10 @@ PGPASSWORD="$POSTGRES_PASSWORD" docker exec -e PGPASSWORD db pg_dump ... corrupt private state, URL rejection and absence of sensitive output. - Run Python compile and shell syntax checks, validate the five-minute calendar, and assert disabled-by-default plus exact managed targets and retained state. +- For a remote rollout, record boolean evidence for pre-write `sudo -n`, exact + temporary-upload removal, temporary privilege removal and post-removal + non-interactive-sudo failure; never record the password, host address or rule + contents. - Re-run `bash scripts/qa/run-automatic-backup-contract.sh` whenever the shared helper allowlist or behavior changes. - ARM install, enabled/active timer status and real failure/recovery messages are diff --git a/.trellis/tasks/07-27-arm-app-health-rollout/check.jsonl b/.trellis/tasks/07-27-arm-app-health-rollout/check.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/07-27-arm-app-health-rollout/check.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. 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."} diff --git a/.trellis/tasks/07-27-arm-app-health-rollout/design.md b/.trellis/tasks/07-27-arm-app-health-rollout/design.md new file mode 100644 index 00000000..28090cdf --- /dev/null +++ b/.trellis/tasks/07-27-arm-app-health-rollout/design.md @@ -0,0 +1,57 @@ +# ARM 应用健康监控上线设计 + +## 1. Safety Boundary + +上线只改变 ARM 上的应用健康探测器、两个 systemd units 和独立状态目录;共用 helper 会更新为仓库已通过备份回归的版本,但不修改 webhook、备份 receiver、dead-man、归档或 OA 主机。所有远端写操作通过非交互 sudo 执行;若预检不满足,任务停在只读阶段。 + +## 2. Source and Secret Provenance + +部署源固定为工作提交 `bb5113c` 中的探测器、helper 和安装器。先确认三个部署源相对 `HEAD` 无未提交改动并复跑专项/备份合同;当前 Trellis 任务记录可按正常流程保持未提交。安装前后比较 SHA-256;不使用远端旧仓库或聊天中的粘贴内容。 + +webhook 文件路径从既有 `hyb-oa-offhost-deadman.service` 的 `ExecStart` 参数在 ARM 内解析。解析结果必须是绝对路径、普通非符号文件、root-owned、mode `0600`;流程从不读取文件正文,也不把路径打印到会话证据。 + +## 3. Rollout Sequence + +```text +local contracts + -> ARM read-only preflight + -> reject pre-existing app-health artifacts + -> upload committed sources to temporary directory + -> sudo installer install (disabled) + -> verify hashes/permissions/units + -> manual real /ready baseline + -> sudo installer enable + -> verify timer + -> stop only app-health timer + -> direct probe #1 against localhost unreachable HTTPS (pending) + -> direct probe #2 (one real failure alert) + -> direct probe #3 (deduplicated) + -> start formal service against public /ready (one recovery) + -> start timer and verify final state + -> remove temporary directory +``` + +失败命令预期非零时必须显式捕获退出码,不能让 shell 把受控故障误判为 rollout 脚本崩溃。状态验证只读取允许字段:schema version、连续失败计数、稳定类别、告警状态和 helper 的 `sent=true|false`。 + +## 4. Drill Isolation + +演练不改 unit、不改 DNS、不停止公网服务。timer 暂停后,以 root 直接执行已安装探测器,目标为 ARM 本机一个预先确认未监听的低风险 TCP 端口,超时 1 秒;其余参数使用正式状态目录、helper 和 webhook。这样覆盖真实 TLS/连接分类、失败阈值、helper、机器人和恢复链,同时不会影响 OA 用户。 + +正式恢复必须通过 systemd service 执行真实公网 `/ready`,从而同时验证已安装 unit。成功后恢复 timer,并再观察至少一次定时健康执行或核对最近成功时间。 + +## 5. Rollback and Alert Closure + +- 安装前失败:无远端写入,仅清理临时目录。 +- 安装后、真实告警前失败:disable/stop 并运行安装器 `uninstall`,保留独立状态证据和共用文件。 +- 真实告警后失败:先移除故障输入并运行正式 service 争取发送恢复通知;成功后可决定保留或卸载。若恢复仍失败,timer 保持停用,明确报告群内告警未闭环,不伪造恢复。 +- 任一路径都不删除共用 helper、webhook、备份 dead-man、receiver 或归档。 + +## 6. Evidence and Privacy + +证据记录提交短哈希、布尔预检、文件哈希比对结果、固定权限、systemd enabled/active、稳定故障类别、连续次数和 helper 发送布尔值。不得保存 SSH 地址、IP、用户名、webhook、完整远端路径、原始 `/ready` 响应、journal 或企微 payload。 + +人工收件确认与自动链路证据分开:脚本显示发送成功不等于用户已经看到消息,最终 AC9 需用户确认。 + +## 7. Compatibility + +安装器会更新共用 helper,因此上线前必须复跑 73 项自动备份合同。上线后只检查既有 dead-man unit/timer 仍存在且状态未变,不主动运行 dead-man,避免归档异常时产生额外真实告警。 diff --git a/.trellis/tasks/07-27-arm-app-health-rollout/implement.jsonl b/.trellis/tasks/07-27-arm-app-health-rollout/implement.jsonl new file mode 100644 index 00000000..9dd3234a --- /dev/null +++ b/.trellis/tasks/07-27-arm-app-health-rollout/implement.jsonl @@ -0,0 +1 @@ +{"_example": "Fill with {\"file\": \"\", \"reason\": \"\"}. 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."} diff --git a/.trellis/tasks/07-27-arm-app-health-rollout/implement.md b/.trellis/tasks/07-27-arm-app-health-rollout/implement.md new file mode 100644 index 00000000..6163606a --- /dev/null +++ b/.trellis/tasks/07-27-arm-app-health-rollout/implement.md @@ -0,0 +1,59 @@ +# ARM 应用健康监控上线执行清单 + +## 1. Local Gates + +- [x] 确认 `HEAD` 包含工作提交 `bb5113c`,三个部署源相对 `HEAD` 无未提交改动;允许当前 Trellis 任务记录保持未提交。 +- [x] 运行应用健康专项 22 项和自动备份兼容回归 73 项。 +- [x] 记录三个部署源文件的 SHA-256,不输出其他本地路径或配置。 + +## 2. Read-only ARM Preflight + +- [x] 通过受控 alias 连接,确认非交互命令可用;首次 sudo 门禁停止,用户建立临时入口后复检通过。 +- [x] 确认 Python 3、systemd、既有 off-host dead-man service/timer 和共用 helper 存在。 +- [x] 在 ARM 内从 dead-man unit 解析 webhook 文件路径,并只返回 owner/mode/regular-file 布尔结果。 +- [x] 确认公网 `/ready` 从 ARM 可达,且应用健康 unit、探测器和状态目录尚不存在;若存在则停止人工审查。 +- [x] 记录既有 dead-man timer enabled/active 基线,后续只做对比,不主动执行。 + +## 3. Disabled Installation + +- [x] 在 ARM 创建本次唯一临时目录,以 SCP 传输探测器、helper 和安装器并核对 SHA-256。 +- [x] 用 root 运行安装器 `install`,webhook 路径只在远端 shell 变量中传递。 +- [x] 确认 timer disabled/inactive、service inactive,unit 为 5 分钟、状态目录 0700、受管文件 root:root 0755。 +- [x] 比较已安装探测器/helper 与提交源哈希一致。 +- [x] 手工启动正式 service,确认健康、probe/alert state 为 0600 且没有发送恢复噪声。 + +## 4. Enable and Real Drill + +- [x] 用同一提交源执行安装器 `enable`,确认 timer enabled/active。 +- [x] 停止应用健康 timer;确认不影响备份 dead-man timer。 +- [x] 确认选定 localhost 演练端口未监听。 +- [x] 第一次直接失败探测:预期非零、`consecutiveFailures=1`、alert pending。 +- [x] 第二次直接失败探测:预期非零、helper `sent=true`、alert state 为 `FAIL_APP_HEALTH`。 +- [x] 第三次直接失败探测:预期非零、helper `sent=false`,证明相同类别降噪。 +- [x] 运行正式 systemd service 恢复:预期成功、helper `sent=true`、alert state 为 `PASS_APP_HEALTH_READY`。 +- [x] 再运行一次健康探测或等待 timer:预期 helper `sent=false`。 +- [x] 重新启动 timer,确认 enabled/active、最近成功和下一次不超过 5 分钟。 + +## 5. Final Evidence and Cleanup + +- [x] 确认既有备份 receiver/dead-man 文件和 timer 状态未改变。 +- [x] 删除本次 ARM 临时传输目录和临时 sudoers 规则,不删除状态、helper、webhook 或备份能力。 +- [x] 仅记录脱敏状态、类别、计数、哈希匹配和 timer 布尔证据。 +- [x] 用户已确认群内实际收到一条故障告警和一条恢复通知。 +- [ ] 更新 PRD 验收项、运行 task validate、治理检查和 `git diff --check`。 + +## 6. Rollback Points + +- [x] 首次 sudo 预检失败时未进入写阶段;用户建立受控临时入口后重新从完整预检开始。 +- [ ] 安装后、故障告警前失败:disable/stop 后 `uninstall`,保留状态和共用能力。 +- [ ] 故障告警后失败:优先正式健康探测关闭告警;无法恢复则保持 timer 停用并明确报告未闭环。 +- [ ] 回滚后复核备份 dead-man timer 与归档仍在,不主动触发 dead-man。 + +## 7. Commands to Validate + +- `bash scripts/qa/run-app-health-monitor-contract.sh` +- `bash scripts/qa/run-automatic-backup-contract.sh` +- `python3 ./.trellis/scripts/task.py validate 07-27-arm-app-health-rollout` +- `node scripts/qa/check-trellis-governance.mjs` +- `node scripts/qa/check-sensitive-content.mjs` +- `git diff --check` diff --git a/.trellis/tasks/07-27-arm-app-health-rollout/prd.md b/.trellis/tasks/07-27-arm-app-health-rollout/prd.md new file mode 100644 index 00000000..260d504b --- /dev/null +++ b/.trellis/tasks/07-27-arm-app-health-rollout/prd.md @@ -0,0 +1,81 @@ +# ARM 应用健康监控上线 + +## Goal + +把已通过本地契约的公网 `/ready` 健康监控安全部署到现有 ARM 异机,启用 5 分钟 timer,并通过一次不影响 OA 用户的真实告警/恢复演练证明“持续故障能发现、重复故障会降噪、恢复会通知”。 + +## Scope + +- 通过现有受控 SSH alias 连接 ARM;先做只读身份、sudo、systemd、既有备份 dead-man 和 webhook/helper 合同预检。 +- 只向临时目录传输已提交版本的探测器、共用告警 helper 和独立安装器;校验源/目标哈希,不传输 webhook 正文。 +- 复用既有 dead-man unit 中已经配置的 root-owned `0600` webhook 文件路径;路径只在 ARM 内解析和校验,不回显其内容。 +- 先执行 `install` 安装 disabled units,核对精确受管目标、权限和 unit 合同,再手工运行一次真实公网健康探测。 +- 健康基线通过后启用 5 分钟 timer;演练期间暂时停止 timer,使用同一已安装探测器、helper、webhook 和生产状态目录对本机不可达 HTTPS 端口连续执行三次:第一次 pending、第二次发送真实故障告警、第三次验证降噪。 +- 随后通过正式 systemd service 探测真实公网 `/ready`,发送一次恢复通知,再恢复 timer 并核对 enabled/active、最近结果和下一次计划时间。 +- 将输出压缩为脱敏状态、类别、计数、布尔检查和提交哈希;不记录 webhook、地址、原始响应、完整 journal 或远端绝对配置路径。 + +## Confirmed Facts + +- 仓库提交 `bb5113c` 已实现并通过应用健康专项 22 项、自动备份兼容回归 73 项、后端 1018 项及前端构建。 +- 安装器固定受管探测器、helper、状态目录和 systemd unit 名;`install` 默认 disabled,`uninstall` 保留共用 helper、webhook 和状态证据。 +- ARM 已有 SSH alias,但当前 alias 不是 root;上线前必须证明非交互 sudo 可用,否则停止,不尝试口令或变更账户。 +- webhook 不在仓库;既有 off-host dead-man service 是复用其路径的唯一事实源。 +- 本任务已获用户确认:允许 ARM 安装、timer 启用、真实企微故障/恢复演练,并在失败时回滚应用健康组件。 + +## Requirements + +- R1:任何写入前先完成只读预检;SSH 身份、`sudo -n`、既有 dead-man unit、root-owned `0600` webhook、Python/systemd 和公网 `/ready` 任一不满足即停止。 +- R2:不得读取、打印或复制 webhook 正文;只允许把 ARM 内部已验证的 webhook 文件路径传给 root 安装器。 +- R3:只安装提交版本的三个文件,并以 SHA-256 比对证明传输和受管目标一致;三个部署源相对 `HEAD` 必须无未提交改动,Trellis 任务记录可按正常流程保持未提交;不得从远端旧副本部署。 +- R4:首次安装发现应用健康 unit、探测器或状态已存在时停止并报告,不覆盖未知运行状态;只有确认是本任务中刚创建的对象后才继续。 +- R5:手工健康探测必须在启用 timer 前成功,且只保留稳定状态字段;不得用本地成功代替 ARM 公网链路成功。 +- R6:真实故障演练不得停止 OA、Caddy、数据库、备份 receiver 或 dead-man;只使用 ARM 本机确定不可达的 HTTPS 端口,并在演练期间停止应用健康 timer 避免竞态。 +- R7:第一次失败不得发送,第二次发送一条,第三次 helper 返回降噪;正式 service 恢复后发送一次恢复通知。 +- R8:最终 timer 必须 enabled/active,最近一次 service 为成功,下一次计划不超过 5 分钟;备份 dead-man timer 仍保持原状态。 +- R9:任一步失败时停止继续扩大变更。若尚未发送故障告警,卸载本任务组件;若已经发送故障告警,优先恢复正式探测并发送恢复通知,失败则保持 timer 停用并明确报告未闭环告警。 + +## Acceptance Criteria + +- [x] AC1:只读预检证明受控 alias 可连接、`sudo -n` 可用、既有 dead-man/webhook/helper 合同成立,且未输出秘密或完整配置路径。 +- [x] AC2:ARM 上安装的探测器和共用 helper 与提交版本哈希一致;unit/状态目录权限符合合同,install 后 timer 明确 disabled/inactive。 +- [x] AC3:ARM 手工正式 `/ready` 探测成功,probe state 为健康且输出无地址、路径、payload 或原始异常。 +- [x] AC4:timer 启用后为 enabled/active,调度周期为 5 分钟且最近正式执行成功。 +- [x] AC5:受控演练证据显示第一次 pending、第二次 sent=true、第三次 sent=false;群内只产生一条脱敏故障告警。 +- [x] AC6:正式 service 恢复成功且 sent=true,后续健康执行 sent=false;群内只产生一条恢复通知。 +- [x] AC7:备份 receiver、dead-man unit/timer、归档与状态均未被卸载或覆盖;备份兼容合同在上线前仍通过。 +- [x] AC8:临时传输目录已清理;最终只保留已批准受管文件、独立状态和 units,工作树与审计记录不含秘密。 +- [x] AC9:用户确认实际收到一条故障告警和一条恢复通知;在此确认前只报告“自动链路证据通过,人工收件待确认”。 + +## Out of Scope + +- 修改 OA、Caddy、数据库、DNS、证书、Compose 或公网部署。 +- 停止真实服务来制造故障。 +- 修改备份 receiver/dead-man 配置、归档、timer 或 webhook 内容。 +- 新建机器人、变更企业微信配置、发送业务数据或原始日志。 +- 增加证书到期预警、业务登录/审批合成监控或第三方 dead-man。 + +## Test Plan + +- 上线前复跑 `bash scripts/qa/run-app-health-monitor-contract.sh` 与 `bash scripts/qa/run-automatic-backup-contract.sh`。 +- ARM 只读预检、源/目标哈希、权限、unit 内容和 state schema 检查均返回有限布尔/状态 token。 +- 先 disabled 安装和健康手工执行,再 enable;演练用同一正式状态链完成 pending/send/dedup/recovery。 +- 结束后检查 timer、service、备份 dead-man 保持情况、临时目录清理、Trellis/task 治理和 `git diff --check`。 + +## Key Decisions + +- 使用“不影响 OA 的本机不可达端口”制造监控输入故障,不停止任何真实服务。 +- 复用正式状态目录做一次完整状态机演练,timer 在演练窗口暂停,避免健康执行插入失败序列。 +- webhook 路径从既有 dead-man unit 在 ARM 内解析,正文始终不读取、不传输、不回显。 +- 用户已批准操作范围,但 Trellis 仍要求本规划摘要之后再明确批准实施。 + +## Execution Evidence + +- 2026-07-27:本地应用健康专项 22 项、自动备份兼容回归 73 项通过;三个部署源相对 `HEAD` 无未提交改动,SHA-256 已记录为脱敏技术证据。 +- 2026-07-27:受控 SSH alias 可连接,但首个 `sudo -n true` 预检返回“需要密码”;流程按 R1 在任何远端写入前停止。 +- 未创建 ARM 临时目录、未上传文件、未修改 systemd、未读取 webhook、未发送真实企微消息。 +- 本地 SSH 配置中未发现指向同一 ARM 的 root alias。继续执行需要用户在 ARM 侧提供非交互 root 运维入口;不得在聊天中发送 sudo/root 密码。 +- 2026-07-27:用户在自己的终端建立临时 sudoers 入口后,完整只读预检通过;disabled 安装、提交哈希、权限和 ARM 公网健康基线均通过,健康基线未发送消息。 +- 2026-07-27:timer 已启用;隔离演练依次得到 pending、`sent=true`、`sent=false`,正式 systemd service 恢复成功并更新为 `PASS_APP_HEALTH_READY`,重复健康为 `sent=false`。 +- 2026-07-27:最终 timer enabled/active、下一次运行在 5 分钟内、最近 service 成功;备份 dead-man enabled/active 未变。临时上传目录和临时 sudoers 规则均已删除。 +- 自动链路已证明一条故障和一条恢复发送成功;随后由用户完成实际群内收件确认。 +- 2026-07-27:用户确认企微群实际收到一条“内部 OA 运维告警”和一条“内部 OA 运维恢复通知”,真实收件闭环完成。 diff --git a/.trellis/tasks/07-27-arm-app-health-rollout/task.json b/.trellis/tasks/07-27-arm-app-health-rollout/task.json new file mode 100644 index 00000000..b4c66553 --- /dev/null +++ b/.trellis/tasks/07-27-arm-app-health-rollout/task.json @@ -0,0 +1,26 @@ +{ + "id": "arm-app-health-rollout", + "name": "arm-app-health-rollout", + "title": "ARM 应用健康监控上线", + "description": "在现有 ARM 异机安全安装并启用公网 /ready 健康监控,通过不影响 OA 用户的真实企微故障、降噪和恢复演练完成上线验收。", + "status": "in_progress", + "dev_type": "operations", + "scope": "deployment", + "package": "hyb-oa", + "priority": "P2", + "creator": "shunli", + "assignee": "shunli", + "createdAt": "2026-07-27", + "completedAt": null, + "branch": null, + "base_branch": "main", + "worktree_path": null, + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [], + "notes": "本地 22+73 项合同通过;首次 sudo 预检安全停止。用户建立临时入口后完成 ARM disabled 安装、启用、真实故障/降噪/恢复演练及最终健康核对,临时上传和 sudoers 已删除;用户确认实际收到一条故障和一条恢复通知。", + "meta": {} +} diff --git a/deploy/trialrun/checklist.md b/deploy/trialrun/checklist.md index 0e4a8c49..a32d1292 100644 --- a/deploy/trialrun/checklist.md +++ b/deploy/trialrun/checklist.md @@ -57,14 +57,14 @@ - [ ] **EXTERNAL_GATED**:已真正停止一次源端 timer 或故意破坏一次传输,ARM dead-man 仅产生一条脱敏企微告警;恢复 finalize 后收到一次恢复通知,同一失败重复运行已降噪 - [ ] 若调整过 `BACKUP_RUN_AT`,已人工确认调整后的次日 UTC 分桶和 26 小时窗口,未把一次预期空窗误判为长期故障 - [ ] 本季度已由第二名内部人员按 runbook 首部恢复卡在 ARM 隔离目标完成影子恢复走查;timer 未自动恢复生产 -- [ ] ARM 公网应用健康监控(未完成远端门禁前保持未勾选): - - [ ] 本地 `bash scripts/qa/run-app-health-monitor-contract.sh` 通过,且自动备份告警契约回归通过 - - [ ] **EXTERNAL_GATED**:已批准 ARM systemd 写入,并确认复用 root-owned `0600` webhook;安装的是已通过自动备份回归的共用 helper 修订,不复制或输出 webhook 正文 - - [ ] `install-offhost-app-health-monitor.sh install` 只安装 disabled units;应用健康 state/alert state 与备份 dead-man 完全独立 - - [ ] 手工 oneshot 健康执行通过,输出不含地址、路径、响应 payload、凭证或原始异常 - - [ ] **EXTERNAL_GATED**:另获批准后执行 `enable`,timer 为 enabled/active,oneshot service 平时 inactive,下一次运行不超过 5 分钟 - - [ ] **EXTERNAL_GATED**:持续失败演练中第一次仅 pending、第二次只发一条、重复失败降噪、恢复只通知一次;未用“机器人能发消息”替代故障链路 - - [ ] 已记录剩余风险:ARM 自身失效会使探测失声,首版没有第三方 dead-man +- [x] ARM 公网应用健康监控(2026-07-27 自动链路与群内收件验收通过): + - [x] 本地 `bash scripts/qa/run-app-health-monitor-contract.sh` 通过,且自动备份告警契约回归通过 + - [x] 已批准 ARM systemd 写入,并确认复用 root-owned `0600` webhook;安装的是已通过自动备份回归的共用 helper 修订,未复制或输出 webhook 正文 + - [x] `install-offhost-app-health-monitor.sh install` 只安装 disabled units;应用健康 state/alert state 与备份 dead-man 完全独立 + - [x] 手工 oneshot 健康执行通过,输出不含地址、路径、响应 payload、凭证或原始异常 + - [x] 已执行 `enable`,timer 为 enabled/active,oneshot 最近执行成功,下一次运行不超过 5 分钟 + - [x] 持续失败演练中第一次仅 pending、第二次发送、重复失败降噪、正式恢复发送、重复健康降噪;用户确认群内收到一条故障和一条恢复通知 + - [x] 已记录剩余风险:ARM 自身失效会使探测失声,首版没有第三方 dead-man - [ ] 停机复制方案已冻结:旧 PostgreSQL 完全停止后复制到空新目录并保留所有权/权限/链接/扩展属性;旧目录原位保留且观察期内不再写入 ## B. 发布(Deploy) diff --git a/deploy/trialrun/runbook.md b/deploy/trialrun/runbook.md index 974cca3b..b02bc20f 100644 --- a/deploy/trialrun/runbook.md +++ b/deploy/trialrun/runbook.md @@ -340,10 +340,12 @@ RESTORE_SCOPE=shadow \ - 确认完整批次已经复制到异机/异盘,而不是仍只保存在应用主机。 - 准备独立 smoke 账号,避免使用老板、管理员或真实员工账号跑自动化。 -## 1.6 ARM 公网应用健康监控(启用仍为 EXTERNAL_GATED) +## 1.6 ARM 公网应用健康监控(已启用) 应用健康监控与自动备份 dead-man 共用受控企微群机器人入口,但使用独立探测器、systemd units、probe state 和 alert state。它从 ARM 每 5 分钟访问公网 HTTPS `/ready`;连续两次失败才告警,一次成功即恢复。不得把它并入备份 receiver 的安装/卸载生命周期,也不得以本地测试通过代替 ARM 已启用证据。 +2026-07-27 已完成 ARM disabled 安装、手工公网健康基线、timer 启用及不影响 OA 的真实故障/降噪/恢复演练;自动链路显示一条故障和一条恢复发送成功,用户确认群内实际收到两条消息。timer 最终 enabled/active、最近 service 成功,备份 dead-man 状态未变,临时上传和临时 sudoers 已删除。 + 先在仓库运行无真实消息合同: ```bash diff --git a/docs/试运行与自动化验证手册.md b/docs/试运行与自动化验证手册.md index 820b3328..55ecdb20 100644 --- a/docs/试运行与自动化验证手册.md +++ b/docs/试运行与自动化验证手册.md @@ -156,7 +156,7 @@ cd client && E2E_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/dg `07-27-app-health-monitoring` 为公网 `/ready` 增加轻量异机探测:ARM 每 5 分钟经标准 HTTPS 校验访问一次;连续两次失败才发送一条脱敏企微群机器人告警,相同类别持续失败不重复发送,恢复后只通知一次。它覆盖 OA 应用、数据库就绪、OA 主机不可达、DNS/Caddy/TLS 已失效等基础故障,不替代登录、审批或报销等业务 smoke,也不执行自动重启或恢复。 -当前仓库代码和本地契约通过,不等于 ARM 已安装或监控已生效。远端 `install`、`enable`、真实消息和故障/恢复演练均为 `EXTERNAL_GATED`,需分别取得批准。先运行本地无真实消息合同: +2026-07-27 已完成 ARM 安装、启用和真实故障/恢复演练:timer 为 enabled/active,首次失败 pending、第二次发送、重复失败降噪、正式恢复发送、重复健康降噪,用户确认群内实际收到一条故障与一条恢复通知;备份 dead-man 未受影响,临时上传和临时 sudoers 已删除。后续重装、真实消息或再次演练仍需单独批准。先运行本地无真实消息合同: ```bash bash scripts/qa/run-app-health-monitor-contract.sh