# 事故报告：公网隧道静默失效 24 天（2026-10-08 发现）

## 现象

用户报告「网址好像变了」/ 打不开。

## 根因

**Cloudflare 主动注销了这个快速隧道**，而 cloudflared 进程**没有退出**。

```
ERR Register tunnel error from server side error="Unauthorized: Tunnel not found"
ERR Connection terminated error="control stream error: Unauthorized: Tunnel not found"
```

- cloudflared 进程从 `2026-09-14 23:36` 一直运行到 `2026-10-08`（**24 天**）
- 但隧道在服务端已被注销，进程只是反复重连失败
- 旧地址 `shadow-beneficial-rca-smallest.trycloudflare.com` **连 DNS 都解析不了**

## 为什么 24 天没人发现

看门狗**只检查 cloudflared 进程是否存在**：

```vbscript
Set cfProcs = svc.ExecQuery("SELECT ProcessId FROM Win32_Process WHERE Name = 'cloudflared.exe'")
If cfProcs.Count = 0 Then ' 启动新的
```

进程僵而不死 → 看门狗认为一切正常 → **永远不会介入**。

这是设计缺陷：**「进程活着」不等于「隧道可用」**。

## 修复

### 1. 看门狗增加真实的健康探测

不再看进程，而是**直接探测公网地址**：

```
本地看板 up + 公网 down  →  隧道坏了  →  杀掉 cloudflared，下一轮重建
连续 2 次失败才动手      →  避免网络抖动误杀
```

### 2. 修复 `start_tunnel.ps1` 的 BOM bug

原用 `Set-Content -Encoding UTF8`，PowerShell 5.1 会写入 **UTF-8 BOM**，
导致 `public_url.txt` 内容变成 `\uFEFFhttps://...`。

看门狗读到带 BOM 的地址后**每个探测都失败** → 会**每 2 分钟误杀一次健康隧道**。

改为 `[System.IO.File]::WriteAllText(..., UTF8Encoding($false))` 无 BOM 写入。
同时在看门狗里加了 `CleanUrl()` 做防御性清洗。

> 这与之前 `local_paths.toml` 的 BOM 问题**是同一类 bug**，
> 已经在运维手册里记过一次，这次又犯在另一个文件上。

### 3. 修复过程本身

- **自己写的测试抓到了 BOM bug**：`PUBLIC probe -> False` 而地址实际可用。
  如果没有这个测试，带上 BOM bug 的看门狗会陷入「杀隧道→重建→再杀」的循环。
- 测试脚本保留在 `deploy\test_watchdog_health.vbs`，可随时复跑验证探测逻辑：
  ```
  LOCAL  probe -> True
  PUBLIC probe -> True
  BOGUS  probe -> False    ← 证明不可达地址能被识别
  DEAD   probe -> False    ← 证明已注销的旧隧道能被识别
  ```

## 我在排查中犯的错误（务必避免重犯）

### 错误 1：用转义引号做进程检查，得到假结果

```powershell
# ❌ 这样写 -Filter 会被破坏，永远返回空 → 假报"看门狗没运行"
@(Get-CimInstance Win32_Process -Filter \"Name='wscript.exe'\" ...).Count
```

我因此**误判看门狗已死，重复启动了 3 个实例**。实际操作是把它写成变量：

```powershell
# ✅ 正确
$filter = "Name='wscript.exe'"
@(Get-CimInstance Win32_Process -Filter $filter | Where-Object { $_.CommandLine -like '*watchdog*' })
```

**同一个坑在 9-17 已经踩过一次**（当时也误判看门狗没运行），这次又踩。
已写入运维手册。

### 错误 2：一开始用 `ADODB.Stream` 读隧道日志

该日志被 cloudflared 的 stderr 重定向**独占打开**，`ADODB.Stream` 读不了，
`On Error Resume Next` 让函数静默返回空 → 健康检查形同虚设。
改用手动探测公网地址后解决。

## 恢复动作

1. 杀掉僵死的 cloudflared（进程启动于 9-14）
2. 从 `tunnel.log` 里找回有效地址（隧道本体其实还活着）
3. 用无 BOM 方式重写 `public_url.txt`
4. 停掉 3 个重复看门狗，只保留 **1 个**运行修复版
5. 同步 `watchdog.vbs` 到启动文件夹

## 当前状态（2026-10-08 23:55）

```
看门狗      : 1 实例（修复版，含公网探测）
隧道进程    : 2 个（1 父 1 子，正常）
看板服务    : 运行中
新地址      : https://operator-pattern-variety-soccer.trycloudflare.com
看板 asOf   : 2026-10-08
公网探测    : HTTP 200，稳定性 6/6
健康检查    : VERDICT OK
```

## 残留风险

1. **快速隧道会被注销** —— 这次是 24 天。修复后看门狗能自动重建，
   但**地址会变**，用户需要重新查询。
2. **看门狗只在登录后运行** —— 主机重启若停在登录界面，仍不会自启。
3. 计划任务需要管理员权限，当前会话无法注册（`Access is denied`）。
