# ADR-062 — 資料安全護欄 (Data Safety Guardrails)

> **狀態**: 已批准 (首席架構師裁示 2026-04-08)
> **撰寫**: Claude Sonnet 4.6 / 2026-04-08 Asia/Taipei
> **相關**: ADR-061 (Alert Operation Log), ADR-058 (SSH Host Repair), ADR-039 (CI/CD)

---

## 背景

Sprint 5.1 啟動前，AWOOOI 的自動修復機制已「移除所有 gate」（2026-04-08 統帥指令），所有 APPROVED Playbook 均可直接執行。此設計在無狀態服務上運作良好，但對**有狀態服務（Stateful Services）**存在資料遺失風險：

- PostgreSQL 重啟 → WAL 截斷、事務回滾
- Redis 重啟 → in-memory 資料遺失、冪等鎖消失
- ClickHouse 重啟 → 列欄檔案損壞

同時，系統缺乏「AI 動作可追查」機制：OpenClaw 的 LLM 推理過程與 alert_operation_log 斷層。

---

## 決策

### D1 — 服務分級（Q1）

建立四級 Stateful 分類，儲存於 `ops/config/service-registry.yaml`（IaC，版控）：

| 等級 | 定義 | 服務 |
|------|------|------|
| `BLOCK` | 系統禁止，僅告警 | PostgreSQL (所有實例), ClickHouse |
| `CRITICAL_HITL` | MultiSig 2票才執行 | Redis, Gitea, Harbor, MinIO |
| `STANDARD_HITL` | 1票審核，特殊 restart 命令 | Prometheus, Grafana, Alertmanager |
| `AUTO` | 自動執行 | Nginx, 所有 K3s Pod, Ollama 等 |

### D2 — Pre-flight 備份檢查（Q2）

針對 `CRITICAL_HITL` 且 `requires_pre_backup=True` 的服務，執行前檢查：
- Velero 最近 Completed 備份 < 4 小時 → 放行
- 過期 → **Abort + 觸發緊急備份（非同步）+ 通知人工**（非直接執行）
- CPU/IO 高負載類告警 → 禁止觸發備份（Q4，防止雪上加霜）

### D3 — MultiSig 雙簽（Q3）

`CRITICAL_HITL` 服務需要 2 票 Telegram 授權才執行，延續 Sprint 3 的 MultiSig 基礎設施。Sprint 5.1 先以同一 user 兩次點擊驗證狀態機，後續 Sprint 擴充多帳號。

### D4 — Prometheus auto_repair flag 優先級（Q9）

- `auto_repair: "false"` → 強制 HITL，AI 無法覆寫
- `auto_repair: "true"` → 進入 AI + Guardrail 評估，Guardrail 有最終否決權

### D5 — Langfuse trace_id 寫入 alert_operation_log（Q10）

OpenClaw 分析完成後，`langfuse_trace_id` 必須寫入 `alert_operation_log.context`，實現 AI 動作全程可追查。

### D6 — AlertManager 不抑制（Q11）

Guardrail 阻擋時，不呼叫 AlertManager silence API。靠 AWOOOI Redis TTL 去重（10 分鐘），AlertManager 持續 firing 保持監控可見性。

---

## 新增 event_type（共 8 個）

```
GUARDRAIL_BLOCKED    # Stateful 服務被阻擋
PRE_FLIGHT_PASSED    # Pre-flight 備份檢查通過
PRE_FLIGHT_FAILED    # Pre-flight 失敗（備份過期）
BACKUP_TRIGGERED     # 緊急備份已觸發
BACKUP_COMPLETED     # 緊急備份完成
BACKUP_FAILED        # 緊急備份失敗
APPROVAL_ESCALATED   # 升級為 CRITICAL_APPROVAL（需 2 票）
CHANGE_APPLIED       # 手動變更已記錄
```

---

## 新增模組

| 模組 | 路徑 | 職責 |
|------|------|------|
| ServiceRegistryClient | `services/service_registry.py` | 讀取 YAML，查詢服務分級 |
| VeleroClient | `services/velero_client.py` | kubectl 查詢備份狀態，觸發緊急備份 |
| PreflightService | `services/preflight_service.py` | Pre-flight 備份檢查邏輯 |

所有模組遵循 leWOOOgo 積木化規範：Service 層，有 `get_xxx()` + `set_xxx()` singleton 模式。

---

## 後果

- 所有有狀態服務的自動修復操作，由「預設允許」改為「預設拒絕，明確授權才執行」
- AI 每一個修復決策都有 Langfuse trace_id 可追查
- Prometheus alert rule 的 `auto_repair` flag 成為有效的安全底線
- 系統複雜度增加，但「資料安全」優先於「修復速度」

---

## 實施計畫

見 `docs/superpowers/plans/2026-04-08-sprint5-data-safety-guardrails.md`（Layer 0~7 完整步驟）

---

## 實施記錄（Sprint 5.1 執行）

### L1-L5 完成（2026-04-08 commit 88696db）

- DB Migration M-002/M-003 已在 192.168.0.188 執行完畢
- `ops/config/service-registry.yaml` 建立（21 個服務分級）
- 三個新 Service 建立：`service_registry.py` / `velero_client.py` / `preflight_service.py`
- Guardrail 注入 `auto_repair_service.py`（BLOCK 等級保守拒絕）
- ALERT_RECEIVED 溯源 + `auto_repair` flag Q9 + Langfuse trace_id Q10 寫入 `webhooks.py`
- `db/models.py` 同步 MultiSig 欄位
- `docker-health-monitor.sh` 純感知層改造（移除所有 docker restart）
- T1-T6 六個 Telegram 通知方法加入 `telegram_gateway.py`

### 首席架構師審查（2026-04-08 commit 0f5fecf）

評分：70/100 → 修正後 90+/100

| 問題 | 級別 | 修正 |
|------|------|------|
| 三個新 service 使用 `logging` 而非 `structlog` | S1 | 改用 `structlog.get_logger()` |
| `velero_client` 使用 `datetime.now(UTC)` | S1 | 改用 `now_taipei()`（台北時區鐵律） |
| Guardrail 失敗時放行（保守原則方向錯誤） | S1 | 失敗時 block，返回 `GUARDRAIL_ERROR` |
| `service_registry` import 在函數內部 | S1 | 移至模組頂部 |
| T1-T6 方法無 try/except | S2 | 全部補齊，失敗 log 不拋出 |
| Langfuse URL 硬寫內網 IP | S2 | 改用 `settings.LANGFUSE_URL` |
| `velero trigger_emergency_backup` kubectl 語法錯誤 | S3 | 改為 `kubectl apply Backup CRD` |
