diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 6c95d69ec..7d0331a45 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,31 @@ +## 2026-06-18|Telegram 通知出口旁路清冊與 Gate + +**背景**:TelegramGateway 最後出口 formatter 已能把 host / runner、Wazuh、Kali、Nginx drift、Backup / Restore / Escrow、Provider freshness 與 supply-chain drift 轉成 `ai_automation_alert_card_v1`,但 workflow、ops script 或 API 旁路若直接呼叫 Telegram Bot API,仍可能繞過 formatter、dedup、outbound mirror、KM / PlayBook / Verifier 與 no-false-green gate。這是 AI 自動化產品不能靠人工記憶補洞的地方,必須先變成可重跑清冊。 + +**完成內容**: +- 新增 `scripts/security/telegram-notification-egress-inventory.py`,repo-only 掃描 `.gitea/workflows`、`scripts/ops`、`scripts/ci`、`apps/api/src`;不讀 secret、不送 Telegram、不改 workflow / script。 +- 新增 `docs/security/telegram-notification-egress-inventory.snapshot.json` 與 `docs/security/TELEGRAM-NOTIFICATION-EGRESS-INVENTORY.md`。 +- `security-mirror-progress-guard.py` 已鎖住 Telegram egress snapshot:`direct_bot_api_call_count=18`、`direct_bot_api_file_count=11`、workflow `13`、ops script `4`、API direct `1`、`gateway_normalized_callsite_count=56`、`gateway_final_exit_formatter_present_count=1`。 +- direct egress 目前分布: + - Gitea workflow:`.gitea/workflows/cd.yaml`、`cd-dev.yaml`、`code-review.yaml`、`deploy-alerts.yaml`、`e2e-health.yaml`、`run-migration.yml` + - Ops script:`scripts/ops/docker-health-monitor.sh`、`pg-backup.sh`、`dr-drill.sh`、`backup-from-110.sh` + - API direct path:`apps/api/src/services/channel_hub.py` + +**邊界**: +- 這是 notification egress inventory / owner gate,不是 workflow 修改、script 修改、`channel_hub.py` 重構、Telegram 實發、Bot API call、secret 讀取、chat route 變更、production deploy 或 runtime gate。 +- owner response received / accepted、formatter convergence accepted、redaction contract accepted、delivery receipt accepted、direct Bot API migration authorized、Telegram send authorized、workflow / script modification authorized、secret collection、raw payload storage、production write、runtime gate、action button 全部維持 `0 / false`。 +- direct Bot API call count 大於 0 代表仍有已知旁路風險;不得把 API 內部 formatter 完成誤判成全域通知出口已完全治理。 + +**驗證**: +- `python3 scripts/security/telegram-notification-egress-inventory.py --root . --generated-at 2026-06-18T22:30:00+08:00 --output docs/security/telegram-notification-egress-inventory.snapshot.json`:`TELEGRAM_NOTIFICATION_EGRESS_INVENTORY_OK direct_calls=18 files=11 workflow=13 ops=4 api=1 runtime_gate=0`。 +- `python3 -m json.tool docs/security/telegram-notification-egress-inventory.snapshot.json`:通過。 + +**完成度同步**: +- Telegram notification egress inventory / guard:`100%`。 +- Direct Bot API formatter convergence:`0%`,需 owner response / maintenance window 後分批處理。 +- AI 自動化告警產品化:通知出口治理從「API final-exit formatter」推進到「repo-wide egress inventory」;下一步是 owner response package 與 migration plan。 +- IwoooS headline 仍維持 `64%`,active runtime gate 仍 `0`。 + ## 2026-06-18|Telegram AIOps 多訊號事件包覆蓋 **背景**:統帥指出 110 CPU 滿載、orphan Chrome、CI build/test 負載這類問題不應靠人工臨場判讀;AWOOOI / AwoooP / IwoooS 必須把監控、告警、PlayBook、KM、修復候選與 verifier 接成真正 AI 自動化產品。既有 host / runner CPU 告警已可轉成 `ai_automation_alert_card_v1`,本段把同一個最後出口規格擴展到 Wazuh、Kali、Nginx drift、Backup / Restore / Escrow、Provider freshness 與 Supply-chain drift。 diff --git a/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md b/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md index 0c597a308..026bc6a61 100644 --- a/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md +++ b/docs/awooop/TELEGRAM-INCIDENT-NOTIFICATION-MODEL.md @@ -83,6 +83,13 @@ Host / runner 資源告警的第一版落地: - formatter 會在 Telegram 最後出口脫敏 URL、內網 IP、絕對路徑、Bearer token、token / password / secret / api key 類 assignment;Top evidence 最多保留三行摘要。 - 這只把 raw signal 變成 AI 自動化候選,不代表允許 Wazuh 封鎖、Kali active scan、`nginx -t` / reload、backup / restore / remote delete、provider switch、package upgrade、workflow dispatch 或 production write。 +通知出口旁路清冊: + +- `docs/security/telegram-notification-egress-inventory.snapshot.json` 目前固定 repo 內 `18` 個 direct Bot API `sendMessage`,分布為 Gitea workflow `13`、ops script `4`、API direct path `1`。 +- 這些 direct egress 可能繞過 TelegramGateway formatter、dedup、outbound mirror、KM / PlayBook / Verifier 與 no-false-green gate;因此只能視為已知待治理 surface,不能視為已完成收斂。 +- 清冊階段只允許保存 path、line、redacted excerpt、owner 欄位與 reviewer gate;不送 Telegram、不讀 Bot token、不保存 chat secret、不改 workflow / script、不開 runtime gate。 +- 後續每個 direct egress 要收斂前,都必須補 owner route、message shape、redaction contract、formatter convergence plan、delivery receipt、fallback mode、rollback owner 與 post-check evidence。 + Host / runner 告警卡片必須採用下列第一屏版型: ```text diff --git a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md index fe83cf141..5a0e4be1d 100644 --- a/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md +++ b/docs/security/HIGH-VALUE-CONFIG-CONTROL-COVERAGE.md @@ -48,6 +48,8 @@ 2026-06-16 再新增 `docs/security/CD-RUNNER-SECRET-INJECTION-POST-INCIDENT-READBACK-PLAN.md` 與 `docs/security/cd-runner-secret-injection-post-incident-readback-plan.snapshot.json`,將同一批 CD pipeline、Code Review、Deploy alerts、Runner attestation 與 Secret parity / injection owner 轉成事故後回讀計畫。固定 `readback_candidate_count=5`、`c0_readback_candidate_count=4`、`c1_readback_candidate_count=1`、`write_capable_readback_candidate_count=5`、`required_readback_field_count=33`、`reviewer_check_count=30`、`outcome_lane_count=11`、`blocked_action_count=52`,讓 `secret_metadata` 從 `68%` 推進到 `70%`,讓 `gitea_workflow_runner_source_control` 從 `72%` 推進到 `74%`,高價值配置平均維持 `71%`,需 live evidence 類別仍為 `9`。此更新只補上 actor、時間窗、workflow diff state、runner executor / host、workspace cleanup、permission scope、secret name parity、secret injection route、step-env secret guard、log redaction、deploy marker / Gitea run、webhook / notification receipt、before / after deploy state、跨專案同步、rollback、post-check、防再發與 no-false-green 的脫敏回讀欄位;post-incident readback received / accepted、workflow 修改、dispatch、runner 變更、repo secret 變更、secret value collection、secret injection change、webhook / deploy key / branch protection / CODEOWNERS 變更、Gitea action dispatch、K8s secret injection、ArgoCD sync、production deploy、runtime gate 與 action button 仍全部為 `0 / false`。 +2026-06-18 再新增 `docs/security/TELEGRAM-NOTIFICATION-EGRESS-INVENTORY.md` 與 `docs/security/telegram-notification-egress-inventory.snapshot.json`,把 repo 內 direct Telegram Bot API `sendMessage` 旁路固定成 notification egress 清冊。固定 `direct_bot_api_call_count=18`、`direct_bot_api_file_count=11`、workflow `13`、ops script `4`、API direct `1`、`gateway_normalized_callsite_count=56`、`gateway_final_exit_formatter_present_count=1`、`required_owner_field_count=18`、`reviewer_check_count=14`、`outcome_lane_count=9`、`blocked_action_count=22`。此更新只表示旁路已可重跑盤點;owner response、formatter convergence accepted、delivery receipt accepted、workflow / script modification、Telegram send、Bot API call、secret collection、production write、runtime gate 與 action button 仍全部為 `0 / false`。 + ## 1.2c 2026-06-18 Backup / Restore / Escrow 事故後回讀計畫 已新增 `docs/security/BACKUP-RESTORE-POST-INCIDENT-READBACK-PLAN.md` 與 `docs/security/backup-restore-post-incident-readback-plan.snapshot.json`,將 38 個 backup / restore / escrow / retention surface 轉成事故後回讀計畫。固定 `readback_candidate_count=38`、`write_capable_readback_candidate_count=27`、`live_evidence_required_readback_candidate_count=38`、`restore_drill_readback_required_candidate_count=38`、`offsite_or_escrow_readback_required_candidate_count=20`、`retention_or_remote_delete_readback_required_candidate_count=17`、`required_readback_field_count=34`、`reviewer_check_count=32`、`outcome_lane_count=11`、`blocked_action_count=51`,讓 `backup_restore_credential` 從 `64%` 推進到 `66%`,高價值配置平均維持 `71%`,需 live evidence 類別仍為 `9`。 diff --git a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md index 2823c760e..79fe42a7a 100644 --- a/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md +++ b/docs/security/IWOOOS-CONFIG-CONTROL-INVENTORY.md @@ -83,6 +83,12 @@ 此更新讓 `secret_metadata` 類別成熟度從 `68%` 推進到 `70%`,讓 `gitea_workflow_runner_source_control` 類別成熟度從 `72%` 推進到 `74%`。它只表示 workflow diff state、runner attestation、executor / host、workspace cleanup、permission scope、secret name parity、secret injection route、step-env secret guard、log redaction、deploy marker / Gitea run、webhook / notification receipt、before / after deploy state、cross-project sync、rollback、post-check、post-change monitoring、recurrence guard 與 no-false-green 已有事故後脫敏回讀欄位;post-incident readback received / accepted、workflow 修改、workflow dispatch、runner 變更、GitHub hosted runner、repo secret 變更、secret value collection、secret injection change、webhook / deploy key / branch protection / CODEOWNERS 變更、Gitea action dispatch、K8s secret injection、ArgoCD sync、production deploy、runtime gate 仍全部為 `0 / false`。 +### 0.3c-2 2026-06-18 Telegram notification egress 旁路清冊 + +`telegram_notification_egress_inventory_v1` 已把 repo 內 direct Telegram Bot API `sendMessage` 旁路納入只讀清冊。固定 `direct_bot_api_file_count=11`、`direct_bot_api_call_count=18`、`workflow_direct_bot_api_call_count=13`、`ops_script_direct_bot_api_call_count=4`、`api_direct_bot_api_call_count=1`、`gateway_normalized_callsite_count=56`、`gateway_final_exit_formatter_present_count=1`。 + +此更新只表示 `.gitea/workflows`、`scripts/ops` 與 `apps/api/src/services/channel_hub.py` 的 direct egress 已可重跑盤點,不代表已遷移。owner response received / accepted、formatter convergence accepted、redaction contract accepted、delivery receipt accepted、direct Bot API migration authorized、Telegram send、Bot API call、workflow / script modification、secret collection、raw payload storage、production write、runtime gate 仍全部為 `0 / false`。 + ### 0.3d 2026-06-15 Public / Admin / API runtime config 變更證據驗收 `public_runtime_config_change_evidence_acceptance_v1` 已把公開產品頁、AwoooP 後台、API / CORS、frontend env、Sentry tunnel、webhook / callback 與跨產品 runtime route 轉成 metadata-only 變更證據驗收只讀帳本。固定 `candidates=6`、`c0=5`、`c1=1`、`write_capable=6`、`source_refs=20`、`required_evidence_fields=21`、`reviewer_checks=21`、`outcome_lanes=8`、`blocked_actions=32`。 diff --git a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md index ef03b6ad2..93d99b2eb 100644 --- a/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md +++ b/docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md @@ -88,6 +88,12 @@ 同步邊界:IwoooS headline 維持 `64%`,active runtime gate 維持 `0`;post-incident readback received / accepted、workflow diff state accepted、runner attestation accepted、secret name parity accepted、secret injection route accepted、deploy marker readback accepted、Gitea run readback accepted、log redaction readback accepted、workflow modification、workflow dispatch、runner change、repo secret change、secret injection change、webhook / deploy key / branch protection / CODEOWNERS change、K8s secret injection、ArgoCD sync、production deploy、runtime gate 與 action buttons 全部仍為 `0 / false`。本段只更新文件、snapshot、guard、覆蓋矩陣、投影契約與前端 marker,不呼叫 Gitea / GitHub API、不讀 secret store、不讀 secret value、不修改 workflow、不啟用 runner、不 rotate secret、不 dispatch workflow、不觸發部署。 +## 0.00aaaa1 2026-06-18 Telegram 通知出口旁路清冊 + +本輪把 repo 內 direct Telegram Bot API `sendMessage` 旁路納入 notification egress 只讀清冊:`telegram_notification_egress_inventory_v1` 固定 `direct_bot_api_file_count=11`、`direct_bot_api_call_count=18`、`workflow_direct_bot_api_call_count=13`、`ops_script_direct_bot_api_call_count=4`、`api_direct_bot_api_call_count=1`、`gateway_normalized_callsite_count=56`、`gateway_final_exit_formatter_present_count=1`、`required_owner_field_count=18`、`reviewer_check_count=14`、`outcome_lane_count=9`、`blocked_action_count=22`。這是 metadata-only 清冊,不是 owner response received / accepted、formatter convergence accepted、delivery receipt accepted、workflow / script modification、Telegram send、Bot API call、secret collection、production write 或 runtime gate。 + +同步邊界:IwoooS headline 維持 `64%`,active runtime gate 維持 `0`;direct Bot API migration authorized、Telegram send authorized、Bot API call authorized、workflow modification authorized、script modification authorized、secret value collection、raw payload storage、production write、runtime gate 與 action buttons 全部仍為 `0 / false`。本段只更新文件、snapshot、guard 與通知模型,不改 workflow、不重構 API、不呼叫 Telegram、不讀 Bot token、不 dispatch workflow、不觸發部署。 + ## 0.00aaa 2026-06-15 K8s / ArgoCD GitOps 變更證據驗收 本輪把 K8s / ArgoCD 從 owner response acceptance 推進到 GitOps 變更證據驗收只讀帳本:`k8s_argocd_change_evidence_acceptance_v1` 固定 `candidates=4`、`c0=3`、`write_capable=4`、`required_evidence_fields=18`、`reviewer_checks=18`、`outcome_lanes=8`、`blocked_actions=28`,並讓 `k8s_production_gitops` 只讀治理成熟度 `62% -> 64%`,高價值配置平均只讀成熟度仍維持 `68%`。這是 metadata-only 收件驗收,不是 change evidence received / accepted、runtime approval package、ArgoCD API read、ArgoCD sync、kubectl action、Helm upgrade、NetworkPolicy apply、NodePort change、RBAC change、live cluster read、production write 或 runtime gate。 diff --git a/docs/security/TELEGRAM-NOTIFICATION-EGRESS-INVENTORY.md b/docs/security/TELEGRAM-NOTIFICATION-EGRESS-INVENTORY.md new file mode 100644 index 000000000..0590947a5 --- /dev/null +++ b/docs/security/TELEGRAM-NOTIFICATION-EGRESS-INVENTORY.md @@ -0,0 +1,110 @@ +# Telegram 通知出口治理清冊 + +| 項目 | 內容 | +|------|------| +| 日期 | 2026-06-18 | +| 狀態 | `inventory_ready_no_runtime_action` | +| 工具 | `scripts/security/telegram-notification-egress-inventory.py` | +| Snapshot | `docs/security/telegram-notification-egress-inventory.snapshot.json` | +| 模式 | repo-only scan;不讀 secret、不送 Telegram、不改 workflow / script | +| runtime gate | `0` | + +## 1. 目的 + +TelegramGateway 已經把 `send_alert_notification()`、`send_text()` 與 `_send_request("sendMessage", ...)` 收斂到最後出口 formatter,能把主機資源壓力、Wazuh、Kali、Nginx drift、Backup / Restore / Escrow、Provider freshness 與 supply-chain drift 轉成 `ai_automation_alert_card_v1`。 + +但 repo 內仍有 workflow、ops script 與一條 API path 直接呼叫 Telegram Bot API。這些路徑可能繞過 TelegramGateway 的 formatter、dedup、outbound mirror、KM / PlayBook / Verifier 沉澱與 no-false-green gate,因此必須先建清冊,再分批收斂。 + +此清冊只建立 metadata-only evidence,不代表允許 live Telegram send、workflow 修改、script 修改、secret 讀取或 production write。 + +## 2. 固定數字 + +| 指標 | 數值 | 解讀 | +|------|------|------| +| `scanned_file_count` | `554` | 掃描 `.gitea/workflows`、`scripts/ops`、`scripts/ci`、`apps/api/src` | +| `direct_bot_api_file_count` | `11` | 仍有 11 個檔案直接呼叫 Bot API | +| `direct_bot_api_call_count` | `18` | 直接 `sendMessage` call site 總數 | +| `workflow_direct_bot_api_call_count` | `13` | Gitea workflow 直送 Telegram | +| `ops_script_direct_bot_api_call_count` | `4` | ops script 直送 Telegram fallback | +| `api_direct_bot_api_call_count` | `1` | API `channel_hub.py` interim path 直送 Telegram | +| `gateway_normalized_callsite_count` | `56` | 已進入 TelegramGateway / final-exit formatter 的 call site | +| `gateway_final_exit_formatter_present_count` | `1` | `normalize_telegram_send_message_payload()` 已存在 | +| `required_owner_field_count` | `18` | 收斂前需要 owner 補齊的欄位 | +| `reviewer_check_count` | `14` | reviewer 必檢規則 | +| `outcome_lane_count` | `9` | 收件結果分流 | +| `blocked_action_count` | `22` | 清冊階段明確禁止動作 | + +所有 `owner_response_received / accepted`、formatter convergence accepted、delivery receipt accepted、Telegram send、Bot API call、workflow modification、script modification、secret collection、raw payload storage、production write、runtime gate 與 action button 都維持 `0 / false`。 + +## 3. 目前 direct Bot API surface + +| 類別 | Call count | 代表路徑 | +|------|------------|----------| +| Gitea workflow | `13` | `.gitea/workflows/cd.yaml`、`.gitea/workflows/cd-dev.yaml`、`.gitea/workflows/code-review.yaml`、`.gitea/workflows/deploy-alerts.yaml`、`.gitea/workflows/e2e-health.yaml`、`.gitea/workflows/run-migration.yml` | +| Ops script | `4` | `scripts/ops/docker-health-monitor.sh`、`scripts/ops/pg-backup.sh`、`scripts/ops/dr-drill.sh`、`scripts/ops/backup-from-110.sh` | +| API direct path | `1` | `apps/api/src/services/channel_hub.py` | + +上述路徑不一定全部錯誤;例如 ops script 可能是 API 離線 fallback。但只要它直接呼叫 Bot API `sendMessage`,就必須被視為通知出口治理 surface:需有 owner、訊息形狀、redaction、formatter convergence、delivery receipt 與 no-false-green 驗收。 + +## 4. Owner 必填欄位 + +每個 direct Bot API surface 收斂前至少需要: + +1. `egress_surface_id` +2. `owner_role_or_team` +3. `routing_purpose` +4. `current_sender` +5. `target_chat_route` +6. `message_shape_contract` +7. `redaction_contract` +8. `formatter_convergence_plan` +9. `delivery_receipt_ref` +10. `dedup_or_fingerprint_plan` +11. `fallback_or_degraded_mode` +12. `migration_or_exception_reason` +13. `maintenance_window` +14. `rollback_owner` +15. `postcheck_evidence_ref` +16. `no_secret_value_attestation` +17. `no_raw_payload_attestation` +18. `no_false_green_attestation` + +所有 evidence 只能是脫敏 ref、commit、run id、job id、artifact pointer 或收件編號;不得貼 Bot token、chat secret、secret hash、partial token、原始訊息 payload、未脫敏 workflow log 或工作視窗內容。 + +## 5. Reviewer checks + +Reviewer 必須確認: + +- direct Bot API surface 已列入 snapshot,不能只靠 `rg` 手工看過。 +- owner role / target route / routing purpose 明確。 +- 訊息形狀要收斂成 `ai_automation_alert_card_v1` 或有明確例外理由。 +- redaction contract、formatter convergence path 與 delivery receipt metadata 存在。 +- fallback mode 不會把 raw payload、完整路徑、內網 IP、secret、工作視窗內容或個人 namespace 送出去。 +- workflow / script / API 修改需另走 owner approval 與維護窗口。 +- 清冊本身沒有送 Telegram、沒有 dispatch workflow、沒有讀 secret。 +- 不把 route `200`、CD success、UI 可見或 Telegram 送達當成 AI 自動化閉環完成。 + +## 6. Outcome lanes + +| Lane | 說明 | +|------|------| +| `waiting_owner_response` | 尚未收到 owner 回覆;所有 accepted / runtime count 維持 0 | +| `request_owner_route_supplement` | 缺 owner、target route 或 routing purpose | +| `request_formatter_convergence_plan` | 缺 formatter convergence 或例外理由 | +| `request_redaction_contract` | 缺 redaction / no-raw-payload attestation | +| `request_delivery_receipt_metadata` | 缺 delivery receipt metadata 或 dedup / fingerprint plan | +| `quarantine_secret_or_raw_payload` | 收到 secret value、raw payload、raw log 或未脫敏截圖時隔離 | +| `reject_false_green_claim` | 把 route 200、CD success、UI 可見或 Telegram 送達當驗收時拒收 | +| `ready_for_notification_egress_review` | metadata 合格後進 reviewer review | +| `waiting_runtime_gate` | 即使 reviewer accepted,runtime send / workflow modification 仍需獨立批准 | + +## 7. 禁止動作 + +此清冊階段明確禁止 Telegram 實發、Bot API call、workflow 修改、未批准 script 修改、secret value / hash / partial token / chat id 收集、保存 raw message payload、保存未脫敏 workflow log、改 chat route、改 Bot token、rotate secret、workflow dispatch、production deploy、把 route 200 / CD success / UI 可見當通知驗收、跳過 formatter convergence、跳過 redaction review、開 runtime gate 或新增 action button。 + +## 8. 下一步 + +1. 先把 Gitea workflow 13 個 direct send 分成 CD / code-review / migration / health 類,決定哪些改走 AWOOI Alertmanager / TelegramGateway,哪些保留為 break-glass fallback。 +2. `scripts/ops/docker-health-monitor.sh`、`pg-backup.sh`、`dr-drill.sh`、`backup-from-110.sh` 需要 owner 確認 API 離線 fallback 的訊息形狀與 redaction contract。 +3. `apps/api/src/services/channel_hub.py` 的 interim Telegram path 需要評估是否改走 TelegramGateway 或至少套同一個 normalization / mirror / receipt 契約。 +4. 每個遷移都必須單獨做 owner approval、maintenance window、rollback owner、no-secret-value evidence 與 post-check;不得用本清冊直接改 workflow 或送 Telegram。 diff --git a/docs/security/telegram-notification-egress-inventory.snapshot.json b/docs/security/telegram-notification-egress-inventory.snapshot.json new file mode 100644 index 000000000..b97ffff2f --- /dev/null +++ b/docs/security/telegram-notification-egress-inventory.snapshot.json @@ -0,0 +1,2059 @@ +{ + "schema_version": "telegram_notification_egress_inventory_v1", + "generated_at": "2026-06-18T22:30:00+08:00", + "git_commit": "27d9f394", + "status": "inventory_ready_no_runtime_action", + "mode": "repo_only_scan_no_secret_value_no_telegram_send", + "scan_roots": [ + ".gitea/workflows", + "scripts/ops", + "scripts/ci", + "apps/api/src" + ], + "summary": { + "scanned_file_count": 554, + "direct_bot_api_file_count": 11, + "direct_bot_api_call_count": 18, + "workflow_direct_bot_api_call_count": 13, + "ops_script_direct_bot_api_call_count": 4, + "ci_script_direct_bot_api_call_count": 0, + "api_direct_bot_api_call_count": 1, + "gateway_normalized_callsite_count": 56, + "gateway_final_exit_formatter_present_count": 1, + "required_owner_field_count": 18, + "reviewer_check_count": 14, + "outcome_lane_count": 9, + "blocked_action_count": 22, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "formatter_convergence_accepted_count": 0, + "redaction_contract_accepted_count": 0, + "delivery_receipt_accepted_count": 0, + "direct_bot_api_migration_authorized_count": 0, + "telegram_send_authorized_count": 0, + "bot_api_call_authorized_count": 0, + "workflow_modification_authorized_count": 0, + "script_modification_authorized_count": 0, + "secret_value_collection_allowed_count": 0, + "raw_payload_storage_allowed_count": 0, + "production_write_authorized_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0 + }, + "execution_boundaries": { + "runtime_execution_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "secret_hash_collection_allowed": false, + "partial_token_collection_allowed": false, + "raw_payload_storage_allowed": false, + "chat_route_change_authorized": false, + "bot_token_change_authorized": false, + "workflow_dispatch_authorized": false, + "production_deploy_authorized": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + "direct_bot_api_calls": [ + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd-dev.yaml:54", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd-dev.yaml", + "line": 54, + "line_hash": "f503c2c0f61100a9", + "sanitized_excerpt": "printf '%b' \"$MSG\" | curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd-dev.yaml:241", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd-dev.yaml", + "line": 241, + "line_hash": "c41f88fbca91a4b9", + "sanitized_excerpt": "printf '%b' \"$MSG\" | curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd-dev.yaml:262", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd-dev.yaml", + "line": 262, + "line_hash": "eaa9a6cb8326dc79", + "sanitized_excerpt": "printf '%b' \"$MSG\" | curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd.yaml:113", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd.yaml", + "line": 113, + "line_hash": "b57e6587a106976b", + "sanitized_excerpt": "curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd.yaml:305", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd.yaml", + "line": 305, + "line_hash": "19c1de8d67af874a", + "sanitized_excerpt": "curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd.yaml:1203", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd.yaml", + "line": 1203, + "line_hash": "6ef020c2b6eac91f", + "sanitized_excerpt": "curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd.yaml:1552", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd.yaml", + "line": 1552, + "line_hash": "d029b95242f39c03", + "sanitized_excerpt": "printf '%b' \"$TG_MSG\" | curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/cd.yaml:1575", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/cd.yaml", + "line": 1575, + "line_hash": "d62c45a8595984fc", + "sanitized_excerpt": "curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/code-review.yaml:137", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/code-review.yaml", + "line": 137, + "line_hash": "a7ebbd735dad5ab2", + "sanitized_excerpt": "curl -fsS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/code-review.yaml:216", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/code-review.yaml", + "line": 216, + "line_hash": "f10f7782dc7c8125", + "sanitized_excerpt": "curl -fsS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/deploy-alerts.yaml:69", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/deploy-alerts.yaml", + "line": 69, + "line_hash": "79e83190f68f27c9", + "sanitized_excerpt": "curl -fS -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/e2e-health.yaml:98", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/e2e-health.yaml", + "line": 98, + "line_hash": "d73ce94678f970a9", + "sanitized_excerpt": "curl -s -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:gitea_workflow_direct_bot_api:.gitea/workflows/run-migration.yml:210", + "surface_kind": "gitea_workflow_direct_bot_api", + "path": ".gitea/workflows/run-migration.yml", + "line": 210, + "line_hash": "934a8dd69fca99b3", + "sanitized_excerpt": "curl -s -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:api_direct_bot_api:apps/api/src/services/channel_hub.py:1138", + "surface_kind": "api_direct_bot_api", + "path": "apps/api/src/services/channel_hub.py", + "line": 1138, + "line_hash": "9e418fa74c83815a", + "sanitized_excerpt": "f\"https://api.telegram.org/bot/sendMessage\",", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:ops_script_direct_bot_api:scripts/ops/backup-from-110.sh:64", + "surface_kind": "ops_script_direct_bot_api", + "path": "scripts/ops/backup-from-110.sh", + "line": 64, + "line_hash": "ff5ffbeca44d679b", + "sanitized_excerpt": "curl -s -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:ops_script_direct_bot_api:scripts/ops/docker-health-monitor.sh:90", + "surface_kind": "ops_script_direct_bot_api", + "path": "scripts/ops/docker-health-monitor.sh", + "line": 90, + "line_hash": "6611406903bc7d65", + "sanitized_excerpt": "curl -s -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:ops_script_direct_bot_api:scripts/ops/dr-drill.sh:63", + "surface_kind": "ops_script_direct_bot_api", + "path": "scripts/ops/dr-drill.sh", + "line": 63, + "line_hash": "b7cf532f3c5509fc", + "sanitized_excerpt": "curl -s -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + }, + { + "egress_surface_id": "telegram_egress:ops_script_direct_bot_api:scripts/ops/pg-backup.sh:52", + "surface_kind": "ops_script_direct_bot_api", + "path": "scripts/ops/pg-backup.sh", + "line": 52, + "line_hash": "8268f3d6f14d66a3", + "sanitized_excerpt": "curl -s -X POST \"https://api.telegram.org/bot/sendMessage\" \\", + "required_owner_fields": [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation" + ], + "reviewer_checks": [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero" + ], + "outcome_lanes": [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate" + ], + "blocked_actions": [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button" + ], + "owner_response_received": false, + "owner_response_accepted": false, + "formatter_convergence_accepted": false, + "redaction_contract_accepted": false, + "delivery_receipt_accepted": false, + "direct_bot_api_migration_authorized": false, + "telegram_send_authorized": false, + "bot_api_call_authorized": false, + "workflow_modification_authorized": false, + "script_modification_authorized": false, + "secret_value_collection_allowed": false, + "raw_payload_storage_allowed": false, + "production_write_authorized": false, + "runtime_gate": false, + "action_buttons_allowed": false, + "not_authorization": true + } + ], + "gateway_normalized_callsite_refs": [ + { + "path": "apps/api/src/api/v1/gitea_webhook.py", + "line": 426, + "line_hash": "2bba62c7403eeae5" + }, + { + "path": "apps/api/src/api/v1/signoz_webhook.py", + "line": 619, + "line_hash": "980d3a75c1e608db" + }, + { + "path": "apps/api/src/jobs/capacity_forecaster_job.py", + "line": 385, + "line_hash": "a7c5d5a21624db48" + }, + { + "path": "apps/api/src/jobs/compliance_scanner_job.py", + "line": 528, + "line_hash": "a5cb293e694f4319" + }, + { + "path": "apps/api/src/jobs/coverage_evaluator_job.py", + "line": 344, + "line_hash": "9d0733502b37bfed" + }, + { + "path": "apps/api/src/jobs/hermes_rule_quality_job.py", + "line": 366, + "line_hash": "ad448bc99ba96d49" + }, + { + "path": "apps/api/src/services/ai_router.py", + "line": 1333, + "line_hash": "202458f20c188326" + }, + { + "path": "apps/api/src/services/approval_execution.py", + "line": 1191, + "line_hash": "5fc34226f6f9d1c7" + }, + { + "path": "apps/api/src/services/converged_alert_recurrence_notifier.py", + "line": 169, + "line_hash": "891a543abef76ca8" + }, + { + "path": "apps/api/src/services/decision_manager.py", + "line": 1220, + "line_hash": "f8477e7b398f9d72" + }, + { + "path": "apps/api/src/services/drift_adopt_service.py", + "line": 475, + "line_hash": "59870f376c6ba34a" + }, + { + "path": "apps/api/src/services/drift_remediator.py", + "line": 227, + "line_hash": "a9a6af0cb71e69f0" + }, + { + "path": "apps/api/src/services/failover_alerter.py", + "line": 261, + "line_hash": "34684a93372ce6d7" + }, + { + "path": "apps/api/src/services/failure_watcher.py", + "line": 747, + "line_hash": "8bee0a47e4dce615" + }, + { + "path": "apps/api/src/services/failure_watcher.py", + "line": 779, + "line_hash": "dcc8fed7fe94b63d" + }, + { + "path": "apps/api/src/services/gitea_webhook_service.py", + "line": 409, + "line_hash": "03ec3600dd9b7e8a" + }, + { + "path": "apps/api/src/services/k3s_monitor_service.py", + "line": 222, + "line_hash": "b8fe78845628308a" + }, + { + "path": "apps/api/src/services/notifications/telegram.py", + "line": 7, + "line_hash": "a5baed9efe0e0f85" + }, + { + "path": "apps/api/src/services/notifications/telegram.py", + "line": 67, + "line_hash": "13a52baf484abbec" + }, + { + "path": "apps/api/src/services/notifications/telegram.py", + "line": 91, + "line_hash": "875d9e9fbc2d75ce" + }, + { + "path": "apps/api/src/services/runbook_generator.py", + "line": 392, + "line_hash": "771939312f164e0c" + }, + { + "path": "apps/api/src/services/runbook_generator.py", + "line": 405, + "line_hash": "813d7d118fc840c4" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 4904, + "line_hash": "2ded92c9f0cf648c" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 5141, + "line_hash": "2c16c0d99e3ba896" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 5574, + "line_hash": "f9fa7599c8c630f3" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 5664, + "line_hash": "0cd3409b20409bd5" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 5667, + "line_hash": "4b46eec88f95b7fc" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 5725, + "line_hash": "435e4b8871b9ea20" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 5784, + "line_hash": "d67a07fbc4e8e104" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6107, + "line_hash": "57dff0f99cd63bb4" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6165, + "line_hash": "2cf36365a86d725d" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6221, + "line_hash": "9724fba690e79a29" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6278, + "line_hash": "c1481223ca6712a7" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6341, + "line_hash": "171855265b7f7b5f" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6416, + "line_hash": "f92bfa489791754e" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6459, + "line_hash": "a7c1d96474e5004c" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 6727, + "line_hash": "1b52a7acba5f263d" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7058, + "line_hash": "e4d15cc17412e1df" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7075, + "line_hash": "ef392666ead2a935" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7131, + "line_hash": "b01759a295fe888d" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7476, + "line_hash": "6de51111c3b3effb" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7633, + "line_hash": "3ae60919d622d66a" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7778, + "line_hash": "098e96b906782f66" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7852, + "line_hash": "cd2982fc71f17ded" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 7946, + "line_hash": "e3c7a73810453996" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8566, + "line_hash": "60fee84eb559944b" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8573, + "line_hash": "d7d36ac583acec88" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8700, + "line_hash": "eef16286b46db389" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8718, + "line_hash": "145af1001d016aef" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8732, + "line_hash": "71d1eae60f8d1eea" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8757, + "line_hash": "891f75aab51d14ce" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8783, + "line_hash": "3d838580c426d3f3" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 8819, + "line_hash": "85016472bc808598" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 9810, + "line_hash": "94c427fd9136122f" + }, + { + "path": "apps/api/src/services/telegram_gateway.py", + "line": 9907, + "line_hash": "b57f776b5430f121" + }, + { + "path": "apps/api/src/services/weekly_report_service.py", + "line": 277, + "line_hash": "aa0200c41090d012" + } + ], + "operator_interpretation": [ + "direct_bot_api_call_count 大於 0 代表仍有 workflow / ops / API 旁路可能繞過 TelegramGateway formatter。", + "本清冊只建立 metadata-only egress surface,不送 Telegram、不修改 workflow / script、不讀 secret value。", + "後續要收斂 direct Bot API 必須另走 owner response、formatter convergence、redaction contract、delivery receipt 與維護窗口。" + ] +} diff --git a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md index f8df42031..f87b9a5ee 100644 --- a/docs/workplans/2026-06-04-iwooos-security-governance-p0.md +++ b/docs/workplans/2026-06-04-iwooos-security-governance-p0.md @@ -82,7 +82,7 @@ | P0-3 | AwoooP 同步封包 | 100% | 已送至 AwoooP 平行工作 thread `019e9154-7d5e-7b72-85be-c9d97e43ecc9`;後續仍需每次推版前重新 fetch / fast-forward | 本文件、thread send readback、mirror checklist readback | | P0-4 | production live sanity 節點 | 100% | desktop / mobile / 展開區塊 / overflow / action href 檢查已完成 | Playwright production sanity 通過 | | P0-5 | LOGBOOK 與完成度更新 | 100% | D2 comments-only、D2 AIOps sample、D2 Code Review 候選分類與 D2 AwoooP Runs fallback 皆已回填;可見 / bundle 變更皆已補 local / production desktop + mobile smoke | `docs/LOGBOOK.md` readback | -| P0-6 | Telegram 監控告警靜音修復 | outbound 主鏈路 100%;靜音 / recurrence slice 88%;整體治理見總表 90% | Alertmanager 缺 project context、既有 approval 收斂告警靜音、AI 分析中重複告警靜音皆已修復並正式 smoke;下一步處理 `polling_active=false` 與非 Alertmanager route project context warning | API health、Telegram health、API pod Alertmanager smoke、production logs `converged_alert_recurrence_sent` | +| P0-6 | Telegram 監控告警 / 通知出口治理 | outbound 主鏈路 100%;靜音 / recurrence slice 88%;通知出口清冊 100%;direct Bot API convergence 0% | Alertmanager 缺 project context、既有 approval 收斂告警靜音、AI 分析中重複告警靜音皆已修復並正式 smoke;`TelegramGateway` final-exit formatter 已完成 host / multi-signal 卡片化;本輪新增 direct Bot API egress inventory,固定 workflow 13、ops script 4、API direct 1,後續需 owner response 後分批收斂,不得把 API formatter 完成誤判成全域完成 | API health、Telegram health、API pod Alertmanager smoke、production logs `converged_alert_recurrence_sent`、`telegram-notification-egress-inventory.snapshot.json` | | P0-7 | Telegram 批准後執行真相鏈止血 | 100% | no-action approval 不再顯示批准 / 執行中;可執行修復 approval 會寫入 `auto_repair_executions`、KM 與 verifier;下一步補 MCP evidence / PlayBook trust 產生真正修復候選 | 目標 pytest `125 passed`、py_compile、guard、production health、API / worker rollout、production pod classifier readback | | P0-8 | Telegram no-action 人工處置包與操作入口 | 100% | no-action 卡片已新增人工處置包、證據補齊清單、AwoooP 修復候選建立步驟、verifier / KM / PlayBook 回寫提醒,並改成 `處置包`、`重診`、`歷史`、`靜默`、`真相鏈`、`Runs` 鍵盤;舊訊息不 retroactive 改寫 | 目標 pytest `64 passed + 44 passed`、py_compile、guard、production health、API / worker rollout、production pod render / keyboard smoke | | P0-9 | MCP evidence -> PlayBook 修復候選產生 | D5 `88%`;Approvals ledger `100%`;Runs ledger desktop `100%`;Alerts ledger desktop / mobile `100%` | 已補 webhook fallback 先建立 incident,再收 MCP evidence、查 approved PlayBook、檢查 trust / command safety、產生 medium approval candidate 與 verifier plan;D1 追加通用兜底 PlayBook / 診斷型命令不可誤當修復、阻擋理由繁中化;D2 在缺候選時產生 `repair_candidate_draft_package_v1`、`playbook_draft_required`、下一步與必填欄位;D3 新增 `awooop_repair_candidate_draft_work_item_v1` read-only projection 與 Telegram `工作項目` deeplink;D4 讓 AwoooP Work Items 詳細呈現 PlayBook 草案處置板、必填欄位、阻擋原因、下一步、Runs / 審批連結;D5 新增 `repair_candidate_coverage_gap_v1`,讓 blocked result 帶出 coverage key、target kind、blocking stage、必收 MCP evidence refs、PlayBook template fields 與 runtime 0 / false 邊界;Approvals / Runs / Alerts 已新增 `資產沉澱` 欄或焦點矩陣,可直接看到 KM / PlayBook / 腳本 / 排程 / Verifier 的完成與卡點;下一步要補 Runs mobile smoke,並把同一總帳接到正式 Telegram 告警卡、Observability 與 Tenants,用真實告警驗證 approval -> execution -> verifier -> KM / PlayBook 回寫 | Approvals code `dafe5342` 已隨 deploy marker `42c08ece` 正式站 desktop / mobile smoke;Runs code `11c2b5d4` 已隨 deploy marker `8b6ab87c` 正式站 desktop DOM smoke;Alerts code `10cd6167` 已隨 deploy marker `d36d764a` 正式站 desktop / mobile DOM smoke;P2-407 API production readback `overall_completion_percent=100`;status-chain 後續仍必須看到 tool call、PlayBook id、risk gate、repair candidate、verifier plan | diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index 4f9384350..1831c1e16 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -228,6 +228,9 @@ def validate(root: Path) -> None: cd_runner_secret_injection_post_incident_readback_plan = load_json( security_dir / "cd-runner-secret-injection-post-incident-readback-plan.snapshot.json" ) + telegram_notification_egress_inventory = load_json( + security_dir / "telegram-notification-egress-inventory.snapshot.json" + ) public_runtime_config_change_evidence_acceptance = load_json( security_dir / "public-runtime-config-change-evidence-acceptance.snapshot.json" ) @@ -21505,6 +21508,128 @@ def validate(root: Path) -> None: f"cd_runner_secret_injection_post_incident_readback_plan.{item['post_incident_readback_candidate_id']}.{false_key}", item[false_key], ) + assert_equal( + "telegram_notification_egress_inventory.schema", + telegram_notification_egress_inventory["schema_version"], + "telegram_notification_egress_inventory_v1", + ) + assert_equal( + "telegram_notification_egress_inventory.status", + telegram_notification_egress_inventory["status"], + "inventory_ready_no_runtime_action", + ) + assert_equal( + "telegram_notification_egress_inventory.mode", + telegram_notification_egress_inventory["mode"], + "repo_only_scan_no_secret_value_no_telegram_send", + ) + expected_telegram_notification_egress_summary = { + "direct_bot_api_file_count": 11, + "direct_bot_api_call_count": 18, + "workflow_direct_bot_api_call_count": 13, + "ops_script_direct_bot_api_call_count": 4, + "ci_script_direct_bot_api_call_count": 0, + "api_direct_bot_api_call_count": 1, + "gateway_normalized_callsite_count": 56, + "gateway_final_exit_formatter_present_count": 1, + "required_owner_field_count": 18, + "reviewer_check_count": 14, + "outcome_lane_count": 9, + "blocked_action_count": 22, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "formatter_convergence_accepted_count": 0, + "redaction_contract_accepted_count": 0, + "delivery_receipt_accepted_count": 0, + "direct_bot_api_migration_authorized_count": 0, + "telegram_send_authorized_count": 0, + "bot_api_call_authorized_count": 0, + "workflow_modification_authorized_count": 0, + "script_modification_authorized_count": 0, + "secret_value_collection_allowed_count": 0, + "raw_payload_storage_allowed_count": 0, + "production_write_authorized_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + } + for key, expected in expected_telegram_notification_egress_summary.items(): + assert_equal( + f"telegram_notification_egress_inventory.summary.{key}", + telegram_notification_egress_inventory["summary"][key], + expected, + ) + expected_telegram_direct_path_counts = { + ".gitea/workflows/cd-dev.yaml": 3, + ".gitea/workflows/cd.yaml": 5, + ".gitea/workflows/code-review.yaml": 2, + ".gitea/workflows/deploy-alerts.yaml": 1, + ".gitea/workflows/e2e-health.yaml": 1, + ".gitea/workflows/run-migration.yml": 1, + "apps/api/src/services/channel_hub.py": 1, + "scripts/ops/backup-from-110.sh": 1, + "scripts/ops/docker-health-monitor.sh": 1, + "scripts/ops/dr-drill.sh": 1, + "scripts/ops/pg-backup.sh": 1, + } + actual_telegram_direct_path_counts: dict[str, int] = {} + for item in telegram_notification_egress_inventory["direct_bot_api_calls"]: + actual_telegram_direct_path_counts[item["path"]] = actual_telegram_direct_path_counts.get(item["path"], 0) + 1 + assert_equal( + "telegram_notification_egress_inventory.direct_path_counts", + actual_telegram_direct_path_counts, + expected_telegram_direct_path_counts, + ) + for key, value in telegram_notification_egress_inventory["execution_boundaries"].items(): + if key == "not_authorization": + assert_true(f"telegram_notification_egress_inventory.execution_boundaries.{key}", value) + else: + assert_false(f"telegram_notification_egress_inventory.execution_boundaries.{key}", value) + for item in telegram_notification_egress_inventory["direct_bot_api_calls"]: + assert_equal( + f"telegram_notification_egress_inventory.{item['egress_surface_id']}.required_owner_fields", + len(item["required_owner_fields"]), + 18, + ) + assert_equal( + f"telegram_notification_egress_inventory.{item['egress_surface_id']}.reviewer_checks", + len(item["reviewer_checks"]), + 14, + ) + assert_equal( + f"telegram_notification_egress_inventory.{item['egress_surface_id']}.outcome_lanes", + len(item["outcome_lanes"]), + 9, + ) + assert_equal( + f"telegram_notification_egress_inventory.{item['egress_surface_id']}.blocked_actions", + len(item["blocked_actions"]), + 22, + ) + assert_true( + f"telegram_notification_egress_inventory.{item['egress_surface_id']}.not_authorization", + item["not_authorization"], + ) + for false_key in [ + "owner_response_received", + "owner_response_accepted", + "formatter_convergence_accepted", + "redaction_contract_accepted", + "delivery_receipt_accepted", + "direct_bot_api_migration_authorized", + "telegram_send_authorized", + "bot_api_call_authorized", + "workflow_modification_authorized", + "script_modification_authorized", + "secret_value_collection_allowed", + "raw_payload_storage_allowed", + "production_write_authorized", + "runtime_gate", + "action_buttons_allowed", + ]: + assert_false( + f"telegram_notification_egress_inventory.{item['egress_surface_id']}.{false_key}", + item[false_key], + ) assert_equal( "public_runtime_config_change_evidence_acceptance.schema", public_runtime_config_change_evidence_acceptance["schema_version"], diff --git a/scripts/security/telegram-notification-egress-inventory.py b/scripts/security/telegram-notification-egress-inventory.py new file mode 100644 index 000000000..6ee829cbf --- /dev/null +++ b/scripts/security/telegram-notification-egress-inventory.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +"""Build a repo-only Telegram notification egress inventory. + +This scanner identifies Telegram Bot API sendMessage paths that can bypass +TelegramGateway's final-exit formatter. It does not read secrets, call +Telegram, modify workflows, or send notifications. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +import sys +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Any + + +TAIPEI = timezone(timedelta(hours=8)) + +SCAN_ROOTS = ( + Path(".gitea/workflows"), + Path("scripts/ops"), + Path("scripts/ci"), + Path("apps/api/src"), +) +SCAN_SUFFIXES = {".py", ".sh", ".js", ".yml", ".yaml"} + +DIRECT_BOT_API_RE = re.compile( + r"api\.telegram\.org/bot.*sendMessage|sendMessage.*api\.telegram\.org/bot" +) +GATEWAY_CALLSITE_RE = re.compile( + r"(?:send_alert_notification\(|\b(?:tg|gw|gateway|telegram)\.send_text\(|_send_request\(\s*[\"']sendMessage[\"'])" +) +SECRET_INTERPOLATION_RE = re.compile(r"\$\{\{\s*secrets\.[^}]+\}\}") +BOT_TOKEN_URL_RE = re.compile(r"api\.telegram\.org/bot.*?/sendMessage") + +REQUIRED_OWNER_FIELDS = [ + "egress_surface_id", + "owner_role_or_team", + "routing_purpose", + "current_sender", + "target_chat_route", + "message_shape_contract", + "redaction_contract", + "formatter_convergence_plan", + "delivery_receipt_ref", + "dedup_or_fingerprint_plan", + "fallback_or_degraded_mode", + "migration_or_exception_reason", + "maintenance_window", + "rollback_owner", + "postcheck_evidence_ref", + "no_secret_value_attestation", + "no_raw_payload_attestation", + "no_false_green_attestation", +] + +REVIEWER_CHECKS = [ + "direct_bot_api_surface_identified", + "owner_role_present", + "target_route_is_sre_owned", + "message_shape_is_ai_automation_card_or_documented_exception", + "redaction_contract_present", + "formatter_convergence_path_present", + "delivery_receipt_metadata_only", + "dedup_or_fingerprint_present", + "fallback_mode_does_not_leak_raw_payload", + "secret_name_only_no_value", + "workflow_or_script_change_requires_separate_approval", + "telegram_send_not_executed_by_inventory", + "no_false_green_claim", + "runtime_gate_stays_zero", +] + +OUTCOME_LANES = [ + "waiting_owner_response", + "request_owner_route_supplement", + "request_formatter_convergence_plan", + "request_redaction_contract", + "request_delivery_receipt_metadata", + "quarantine_secret_or_raw_payload", + "reject_false_green_claim", + "ready_for_notification_egress_review", + "waiting_runtime_gate", +] + +BLOCKED_ACTIONS = [ + "telegram_send", + "bot_api_call", + "workflow_modification", + "script_modification_without_owner", + "secret_value_collection", + "secret_hash_collection", + "partial_token_collection", + "chat_id_collection_without_owner", + "store_raw_message_payload", + "store_unredacted_workflow_log", + "change_chat_route", + "change_bot_token", + "rotate_secret", + "workflow_dispatch", + "production_deploy", + "accept_route_200_as_delivery_receipt", + "accept_cd_success_as_notification_acceptance", + "accept_ui_visible_as_notification_acceptance", + "skip_formatter_convergence", + "skip_redaction_review", + "open_runtime_gate", + "add_action_button", +] + + +def git_short_sha(root: Path) -> str: + try: + result = subprocess.run( + ["git", "rev-parse", "--short", "HEAD"], + cwd=root, + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip() + except Exception: + return "unknown" + + +def iter_scannable_files(root: Path) -> list[Path]: + files: list[Path] = [] + for scan_root in SCAN_ROOTS: + absolute_root = root / scan_root + if not absolute_root.exists(): + continue + for path in absolute_root.rglob("*"): + if path.is_file() and path.suffix in SCAN_SUFFIXES: + files.append(path) + return sorted(files) + + +def sanitize_excerpt(line: str) -> str: + excerpt = line.strip() + excerpt = SECRET_INTERPOLATION_RE.sub("${{ secrets. }}", excerpt) + excerpt = BOT_TOKEN_URL_RE.sub("api.telegram.org/bot/sendMessage", excerpt) + return excerpt[:180] + + +def surface_kind(relative_path: str) -> str: + if relative_path.startswith(".gitea/workflows/"): + return "gitea_workflow_direct_bot_api" + if relative_path.startswith("scripts/ops/"): + return "ops_script_direct_bot_api" + if relative_path.startswith("scripts/ci/"): + return "ci_script_direct_bot_api" + if relative_path.startswith("apps/api/src/"): + return "api_direct_bot_api" + return "other_direct_bot_api" + + +def line_hash(relative_path: str, line_number: int, line: str) -> str: + payload = f"{relative_path}:{line_number}:{line.strip()}".encode("utf-8") + return hashlib.sha256(payload).hexdigest()[:16] + + +def build_report(root: Path, generated_at: str | None = None) -> dict[str, Any]: + generated = generated_at or datetime.now(TAIPEI).isoformat(timespec="seconds") + files = iter_scannable_files(root) + direct_calls: list[dict[str, Any]] = [] + gateway_calls: list[dict[str, Any]] = [] + + for path in files: + relative_path = path.relative_to(root).as_posix() + text = path.read_text(encoding="utf-8", errors="replace") + for line_number, line in enumerate(text.splitlines(), start=1): + if DIRECT_BOT_API_RE.search(line): + kind = surface_kind(relative_path) + direct_calls.append( + { + "egress_surface_id": f"telegram_egress:{kind}:{relative_path}:{line_number}", + "surface_kind": kind, + "path": relative_path, + "line": line_number, + "line_hash": line_hash(relative_path, line_number, line), + "sanitized_excerpt": sanitize_excerpt(line), + "required_owner_fields": REQUIRED_OWNER_FIELDS, + "reviewer_checks": REVIEWER_CHECKS, + "outcome_lanes": OUTCOME_LANES, + "blocked_actions": BLOCKED_ACTIONS, + "owner_response_received": False, + "owner_response_accepted": False, + "formatter_convergence_accepted": False, + "redaction_contract_accepted": False, + "delivery_receipt_accepted": False, + "direct_bot_api_migration_authorized": False, + "telegram_send_authorized": False, + "bot_api_call_authorized": False, + "workflow_modification_authorized": False, + "script_modification_authorized": False, + "secret_value_collection_allowed": False, + "raw_payload_storage_allowed": False, + "production_write_authorized": False, + "runtime_gate": False, + "action_buttons_allowed": False, + "not_authorization": True, + } + ) + + if GATEWAY_CALLSITE_RE.search(line): + gateway_calls.append( + { + "path": relative_path, + "line": line_number, + "line_hash": line_hash(relative_path, line_number, line), + } + ) + + direct_files = sorted({item["path"] for item in direct_calls}) + workflow_direct_calls = [item for item in direct_calls if item["surface_kind"] == "gitea_workflow_direct_bot_api"] + ops_direct_calls = [item for item in direct_calls if item["surface_kind"] == "ops_script_direct_bot_api"] + ci_direct_calls = [item for item in direct_calls if item["surface_kind"] == "ci_script_direct_bot_api"] + api_direct_calls = [item for item in direct_calls if item["surface_kind"] == "api_direct_bot_api"] + telegram_gateway_path = root / "apps/api/src/services/telegram_gateway.py" + telegram_gateway_text = telegram_gateway_path.read_text(encoding="utf-8", errors="replace") + gateway_formatter_present = "normalize_telegram_send_message_payload" in telegram_gateway_text + + return { + "schema_version": "telegram_notification_egress_inventory_v1", + "generated_at": generated, + "git_commit": git_short_sha(root), + "status": "inventory_ready_no_runtime_action", + "mode": "repo_only_scan_no_secret_value_no_telegram_send", + "scan_roots": [path.as_posix() for path in SCAN_ROOTS], + "summary": { + "scanned_file_count": len(files), + "direct_bot_api_file_count": len(direct_files), + "direct_bot_api_call_count": len(direct_calls), + "workflow_direct_bot_api_call_count": len(workflow_direct_calls), + "ops_script_direct_bot_api_call_count": len(ops_direct_calls), + "ci_script_direct_bot_api_call_count": len(ci_direct_calls), + "api_direct_bot_api_call_count": len(api_direct_calls), + "gateway_normalized_callsite_count": len(gateway_calls), + "gateway_final_exit_formatter_present_count": 1 if gateway_formatter_present else 0, + "required_owner_field_count": len(REQUIRED_OWNER_FIELDS), + "reviewer_check_count": len(REVIEWER_CHECKS), + "outcome_lane_count": len(OUTCOME_LANES), + "blocked_action_count": len(BLOCKED_ACTIONS), + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "formatter_convergence_accepted_count": 0, + "redaction_contract_accepted_count": 0, + "delivery_receipt_accepted_count": 0, + "direct_bot_api_migration_authorized_count": 0, + "telegram_send_authorized_count": 0, + "bot_api_call_authorized_count": 0, + "workflow_modification_authorized_count": 0, + "script_modification_authorized_count": 0, + "secret_value_collection_allowed_count": 0, + "raw_payload_storage_allowed_count": 0, + "production_write_authorized_count": 0, + "runtime_gate_count": 0, + "action_button_count": 0, + }, + "execution_boundaries": { + "runtime_execution_authorized": False, + "telegram_send_authorized": False, + "bot_api_call_authorized": False, + "workflow_modification_authorized": False, + "script_modification_authorized": False, + "secret_value_collection_allowed": False, + "secret_hash_collection_allowed": False, + "partial_token_collection_allowed": False, + "raw_payload_storage_allowed": False, + "chat_route_change_authorized": False, + "bot_token_change_authorized": False, + "workflow_dispatch_authorized": False, + "production_deploy_authorized": False, + "action_buttons_allowed": False, + "not_authorization": True, + }, + "direct_bot_api_calls": direct_calls, + "gateway_normalized_callsite_refs": gateway_calls, + "operator_interpretation": [ + "direct_bot_api_call_count 大於 0 代表仍有 workflow / ops / API 旁路可能繞過 TelegramGateway formatter。", + "本清冊只建立 metadata-only egress surface,不送 Telegram、不修改 workflow / script、不讀 secret value。", + "後續要收斂 direct Bot API 必須另走 owner response、formatter convergence、redaction contract、delivery receipt 與維護窗口。", + ], + } + + +def validate(root: Path) -> None: + report = build_report(root) + if report["summary"]["gateway_final_exit_formatter_present_count"] != 1: + raise SystemExit("BLOCKED telegram egress inventory: gateway formatter not found") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build Telegram notification egress inventory") + parser.add_argument("--root", default=".", help="repository root") + parser.add_argument("--output", help="write JSON snapshot") + parser.add_argument("--generated-at", help="fixed generated_at timestamp") + args = parser.parse_args() + + root = Path(args.root).resolve() + report = build_report(root, args.generated_at) + payload = json.dumps(report, ensure_ascii=False, indent=2) + "\n" + if args.output: + Path(args.output).write_text(payload, encoding="utf-8") + else: + sys.stdout.write(payload) + + print( + "TELEGRAM_NOTIFICATION_EGRESS_INVENTORY_OK " + f"direct_calls={report['summary']['direct_bot_api_call_count']} " + f"files={report['summary']['direct_bot_api_file_count']} " + f"workflow={report['summary']['workflow_direct_bot_api_call_count']} " + f"ops={report['summary']['ops_script_direct_bot_api_call_count']} " + f"api={report['summary']['api_direct_bot_api_call_count']} " + f"runtime_gate={report['summary']['runtime_gate_count']}", + file=sys.stderr, + ) + + +if __name__ == "__main__": + main()