Merge remote-tracking branch 'origin/main' into codex/p0-obs-002-20260715

This commit is contained in:
ogt
2026-07-15 01:08:44 +08:00
101 changed files with 12831 additions and 1287 deletions

View File

@@ -1,5 +1,9 @@
# ADR-093: Telegram 告警全面遷移至 SRE 戰情室群組
> **2026-07-14 sunset**:本 ADR 的「所有產品/未知類型預設進 SRE 群組」已由 `global_product_governance_v2` 取代。
> `superseded_by=global_product_governance_v2`owner=`sre_shared_runtime`expiry=`2026-07-14T14:35:26+08:00`replacement=`docs/security/telegram-canonical-routing-registry.snapshot.json`verifier=`apps/api/tests/test_telegram_canonical_routing_registry.py`。
> 保留的唯一行為是 allowlisted AWOOOI shared infra/security/DR/P0-P1 lifecycle未知 route、raw P2/P3、marketing 與跨產品 fallback 一律 blocked。
> **狀態**: ✅ 已全面切換
> **日期**: 2026-04-24
> **決策者**: 統帥 + 12-Agent 全景分析團隊onboarder / debugger / db-expert / tool-expert / web-researcher / planner / critic / frontend-designer / fullstack-engineer / refactor-specialist / migration-engineer / vuln-verifier
@@ -8,7 +12,7 @@
原 Telegram 告警雙軌路由設計:
- 所有告警類TYPE-3/4/4D/8M預設發私人 DM `OPENCLAW_TG_CHAT_ID`
- 僅心跳報告、週月報、AI 三頭分析發群組 `SRE_GROUP_CHAT_ID=-1003711974679`
- 僅心跳報告、週月報、AI 三頭分析發群組 alias `SRE_GROUP_CHAT_ID`numeric ID 不進文件)
- `_interactive_types` 黑名單曾把互動式告警擋在群組外
使用者需求:把**所有**告警(含需人工簽核的 TYPE-3/4/4D/8M遷到 AwoooI SRE 戰情室群組4 名成員),以便團隊共同監察。

View File

@@ -1,6 +1,6 @@
# AWOOOI 全棧冷啟動與主機重啟 SOP
> Version: v1.134
> Version: v1.136
> Last updated: 2026-07-14 Asia/Taipei
> Scope: 99 / 110 / 111 / 112 / 120 / 121 / 188 全棧重啟恢復。112 仍是 Kali / VM guest 訊號,但 2026-06-30 全主機重啟後已納入 10 分鐘 SLO 的必要 boot / power signal此納入不代表授權任何破壞性 runtime apply。
@@ -10,6 +10,20 @@
本節是每次接手、開機、關機、重啟後的第一個判定錨點。若日期不是今天,必須先重跑 live check再更新本節與 `docs/workplans/2026-06-04-reboot-cold-start-backup-recovery-workplan.md`
### v1.136 2026-07-14 Host112 正式納入 cold-start P0 release gate
`full-stack-cold-start-check.sh` 舊版仍顯示 `112 Kali is intentionally skipped`,與 Windows99 五台 required VM、Agent99 `host112_guest_readiness` phase 及全主機 10 分鐘 SLO 不一致。現行 baseline 已把 112 從 excluded host 移入 included hostP0 network 固定檢查 ping / TCP 22並透過 `kali@192.168.0.112` 執行 canonical `/usr/local/sbin/awoooi-host112-guest-readiness --check`。release gate 必須同時驗證 readback schema、systemd running、guest/console/services/recovery timer、Wazuh 1514/1515/55000 independent verifier 與 `runtime_write_performed=0`。任何一項失敗均維持 BLOCKED不得因其他四台 Linux host 或 public routes 為 200 而宣稱全主機恢復。110 textfile exporter scope 固定為 `110_112_120_121_188`,並分開輸出 112 host unreachable 與 guest readiness failed blocker series`verify-cold-start-monitor-deploy.sh` 必須驗證 source/live hash、scope 與 blocker series parity避免 checker 已納管但 Prometheus 仍漏掉 112。
### v1.135 2026-07-14 Host188 n8n / Open WebUI 502 自動恢復
本輪全資產 public smoke 發現 `https://n8n.wooo.work``https://ollama.wooo.work` 均為 raw `502`。188 的 Nginx 正常,但 upstream `127.0.0.1:5678` / `127.0.0.1:3010` 沒有可用服務Ansible 宣稱 `/opt/n8n``/opt/open-webui` 可直接 `docker compose up`live 目錄卻不存在。n8n 的實際資料與 base compose 在 `/home/ollama/n8n`Open WebUI 只剩 `open-webui` volume兩個本機映像都已遺失。這類事件必須分類為「服務部署資產漂移」不能只重啟 Nginx、不能把 502 當 warmup也不能從外部 registry 拉 mutable `latest`
- 受控恢復固定使用內部 Harbor immutable digestn8n `sha256:dbe732489e5b8941aa89ca71b320ccb7f80319fcccd0f2c9e05f561562d804e5`Open WebUI `sha256:a673af6e7fab29822a0d32d71184d3f0277347a693633c86372643590c921915`。apply 前先建立 run-scoped n8n data / Open WebUI volume 快照,任何資料 restore 仍是 break-glass不得自動執行。
- 188 Docker 啟用 `userns-remap``dockremap` 起始 UID/GID 為 `100000`。n8n image 的 `node` UID/GID `1000` 必須映射成 host `101000:101000`;直接加 `userns_mode: host` 會讓 image layer 內 `/home/node/.cache` 再次 EACCES因此正式修法是收斂 bind data ownership、維持預設 user namespace。
- canonical executor 是 `scripts/reboot-recovery/host188-edge-services-recover.sh`live path `/home/ollama/bin/agent99-host188-edge-services-recover.sh``--check` 僅讀 container/image/restart/health、本機 upstream 與 local-Nginx HTTPS`--apply` 才可拉固定 Harbor digest、建立快照、原子寫入 managed compose、修正 n8n remap ownership、啟動服務再跑獨立 `--check`
- live apply `trace-agent99-edge-services-20260714 / run-agent99-edge-services-20260714-02 / AIA-P0-006-EDGE-502-188` 終態為 `deployed_verified`n8n 與 Open WebUI 均 `running`、restart count `0`、local/public HTTP `200`、config managed、image pinnedreceipt 在 `/home/ollama/agent99-runtime-recovery/run-agent99-edge-services-20260714-02/receipt.json`。後續 no-write check 為 exit `0``runtimeWritePerformed=false`,兩份 compose mtime 不變。
- Agent99 `Status` 必須偵測此 lane 並排入單一 RecoverRecover 固定執行 Detect -> check -> bounded apply -> independent verifier -> callback/TG/KM。`full-stack-cold-start-check.sh` 與 AI log triage public routes 必須同時包含 n8n / Open WebUI兩者任一未通過時不得宣稱所有網站恢復。
### v1.134 2026-07-14 Agent99 canonical host readback closure
本輪釐清 111 的 `perf_readback_failed` 不是 CPU 壓力,而是 Agent99 沿用預設 `wooo`,但 110 到 111 的 canonical restricted identity 是 `ooo`。部署合約固定 `sshUsers["192.168.0.111"]="ooo"`,舊 live config 必須由 `host111_canonical_ssh_user` migration 寫入後重新讀回帳號、transport 或四項指標任一缺失時,只能標示 readback degraded不得產生 CPU/記憶體/磁碟故障結論或執行降載。

View File

@@ -0,0 +1,309 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://awoooi.wooo.work/schemas/telegram_canonical_routing_registry_v1.schema.json",
"title": "Telegram Canonical Routing Registry v1",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"generated_at",
"status",
"mode",
"supersedes",
"policy",
"destination_roles",
"products",
"routes",
"rollups",
"execution_boundaries"
],
"properties": {
"schema_version": {
"const": "telegram_canonical_routing_registry_v1"
},
"generated_at": {"type": "string", "format": "date-time"},
"status": {"const": "source_policy_ready_runtime_not_applied"},
"mode": {"const": "source_only_no_secret_no_telegram_send_no_route_change"},
"supersedes": {
"type": "object",
"additionalProperties": false,
"required": [
"policy_ref",
"superseded_behavior",
"superseded_by",
"owner",
"expiry",
"replacement",
"verifier"
],
"properties": {
"policy_ref": {"type": "string", "minLength": 1},
"superseded_behavior": {"type": "string", "minLength": 1},
"superseded_by": {"const": "global_product_governance_v2"},
"owner": {"type": "string", "minLength": 1},
"expiry": {"type": "string", "format": "date-time"},
"replacement": {"type": "string", "minLength": 1},
"verifier": {"type": "string", "minLength": 1}
}
},
"policy": {
"type": "object",
"additionalProperties": false,
"required": [
"default_decision",
"unknown_product_decision",
"unknown_signal_family_decision",
"unknown_severity_decision",
"unknown_destination_decision",
"legacy_runtime_resolver",
"legacy_shared_sre_allowed_types",
"legacy_shared_sre_blocked_types",
"raw_chat_id_allowed",
"shared_sre_rule",
"shared_sre_forbidden"
],
"properties": {
"default_decision": {"const": "block"},
"unknown_product_decision": {"const": "block"},
"unknown_signal_family_decision": {"const": "block"},
"unknown_severity_decision": {"const": "block"},
"unknown_destination_decision": {"const": "block"},
"legacy_runtime_resolver": {"type": "string", "minLength": 1},
"legacy_shared_sre_allowed_types": {
"type": "array",
"minItems": 7,
"maxItems": 7,
"uniqueItems": true,
"items": {
"enum": ["TYPE-2", "TYPE-3", "TYPE-4", "TYPE-4D", "TYPE-5S", "TYPE-7E", "TYPE-8M"]
}
},
"legacy_shared_sre_blocked_types": {
"type": "array",
"minItems": 3,
"maxItems": 3,
"uniqueItems": true,
"items": {"enum": ["TYPE-1", "TYPE-6B", "UNKNOWN"]}
},
"raw_chat_id_allowed": {"const": false},
"shared_sre_rule": {"type": "string", "minLength": 1},
"shared_sre_forbidden": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {"type": "string", "minLength": 1}
}
}
},
"destination_roles": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"propertyNames": {
"not": {
"pattern": "(?:[Cc][Hh][Aa][Tt][_-]?[Ii][Dd]|[Tt][Oo][Kk][Ee][Nn]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Aa][Uu][Tt][Hh][Oo][Rr][Ii][Zz][Aa][Tt][Ii][Oo][Nn]|[Ww][Ee][Bb][Hh][Oo][Oo][Kk])"
}
},
"required": [
"visible_label",
"proven_runtime_alias",
"runtime_alias_evidence",
"chat_alias",
"bot_alias",
"bot_owner",
"monitoring_owner",
"role",
"monitoring_egress_policy",
"runtime_identifier_in_registry"
],
"properties": {
"visible_label": {
"type": "string",
"minLength": 1,
"pattern": "^(?!-?[0-9]{8,}$).+$"
},
"proven_runtime_alias": {
"type": "string",
"pattern": "^(?!-?[0-9]{8,}$)[a-z0-9_]+$"
},
"runtime_alias_evidence": {
"enum": [
"proven_runtime_receipt",
"source_config_only",
"configured_unreachable",
"visible_label_only"
]
},
"chat_alias": {
"type": "string",
"pattern": "^(?!-?[0-9]{8,}$)[a-z0-9_]+$"
},
"bot_alias": {
"type": "string",
"pattern": "^(?!-?[0-9]{8,}$)[a-z0-9_]+$"
},
"bot_owner": {"type": "string", "minLength": 1},
"monitoring_owner": {"type": "boolean"},
"role": {"type": "string", "minLength": 1},
"monitoring_egress_policy": {"type": "string", "minLength": 1},
"runtime_identifier_in_registry": {"const": false}
}
}
},
"products": {
"type": "array",
"minItems": 11,
"maxItems": 11,
"items": {
"type": "object",
"additionalProperties": false,
"propertyNames": {
"not": {
"pattern": "(?:[Cc][Hh][Aa][Tt][_-]?[Ii][Dd]|[Tt][Oo][Kk][Ee][Nn]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Aa][Uu][Tt][Hh][Oo][Rr][Ii][Zz][Aa][Tt][Ii][Oo][Nn]|[Ww][Ee][Bb][Hh][Oo][Oo][Kk])"
}
},
"required": [
"product_id",
"display_name",
"owner",
"source_audit_status",
"egress_status"
],
"properties": {
"product_id": {"type": "string", "minLength": 1},
"display_name": {"type": "string", "minLength": 1},
"owner": {"type": "string", "minLength": 1},
"source_audit_status": {"type": "string", "minLength": 1},
"egress_status": {
"enum": ["allowed", "blocked", "disabled", "not_implemented"]
}
}
}
},
"routes": {
"type": "array",
"minItems": 11,
"items": {
"type": "object",
"additionalProperties": false,
"propertyNames": {
"not": {
"pattern": "(?:[Cc][Hh][Aa][Tt][_-]?[Ii][Dd]|[Tt][Oo][Kk][Ee][Nn]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Aa][Uu][Tt][Hh][Oo][Rr][Ii][Zz][Aa][Tt][Ii][Oo][Nn]|[Ww][Ee][Bb][Hh][Oo][Oo][Kk])"
}
},
"required": [
"route_id",
"product_id",
"signal_family",
"severity",
"scope",
"bot_alias",
"chat_alias",
"allowed_destination",
"receipt_backend",
"owner",
"egress_status"
],
"properties": {
"route_id": {"type": "string", "minLength": 1},
"product_id": {"type": "string", "minLength": 1},
"signal_family": {"type": "string", "minLength": 1},
"severity": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {"enum": ["P0", "P1", "P2", "P3"]}
},
"scope": {"enum": ["shared", "product"]},
"bot_alias": {
"type": "string",
"pattern": "^(?!-?[0-9]{8,}$)[a-z0-9_]+$"
},
"chat_alias": {
"type": "string",
"pattern": "^(?!-?[0-9]{8,}$)[a-z0-9_]+$"
},
"allowed_destination": {
"type": "string",
"pattern": "^(?:blocked|telegram_chat_alias:[a-z0-9_]+)$"
},
"receipt_backend": {
"type": "string",
"minLength": 1,
"pattern": "^(?!-?[0-9]{8,}$).+$"
},
"owner": {"type": "string", "minLength": 1},
"egress_status": {
"enum": ["allowed", "blocked", "disabled", "not_implemented"]
},
"blocked_reason": {"type": "string", "minLength": 1}
},
"allOf": [
{
"if": {"properties": {"egress_status": {"const": "allowed"}}},
"then": {
"properties": {
"allowed_destination": {
"pattern": "^telegram_chat_alias:[a-z0-9_]+$"
}
}
},
"else": {
"required": ["blocked_reason"],
"properties": {"allowed_destination": {"const": "blocked"}}
}
}
]
}
},
"rollups": {
"type": "object",
"additionalProperties": false,
"required": [
"product_count",
"route_count",
"allowed_route_count",
"blocked_route_count",
"disabled_route_count",
"not_implemented_route_count",
"shared_sre_allowed_route_count",
"raw_numeric_destination_count",
"runtime_route_change_count",
"telegram_send_count"
],
"properties": {
"product_count": {"const": 11},
"route_count": {"type": "integer", "minimum": 11},
"allowed_route_count": {"type": "integer", "minimum": 0},
"blocked_route_count": {"type": "integer", "minimum": 0},
"disabled_route_count": {"type": "integer", "minimum": 0},
"not_implemented_route_count": {"type": "integer", "minimum": 0},
"shared_sre_allowed_route_count": {"type": "integer", "minimum": 0},
"raw_numeric_destination_count": {"const": 0},
"runtime_route_change_count": {"const": 0},
"telegram_send_count": {"const": 0}
}
},
"execution_boundaries": {
"type": "object",
"additionalProperties": false,
"required": [
"telegram_send_allowed",
"bot_api_call_allowed",
"secret_value_read_allowed",
"raw_chat_id_storage_allowed",
"runtime_route_change_allowed",
"production_deploy_allowed"
],
"properties": {
"telegram_send_allowed": {"const": false},
"bot_api_call_allowed": {"const": false},
"secret_value_read_allowed": {"const": false},
"raw_chat_id_storage_allowed": {"const": false},
"runtime_route_change_allowed": {"const": false},
"production_deploy_allowed": {"const": false}
}
}
}
}

View File

@@ -0,0 +1,94 @@
# Telegram 監控告警路由總帳
> 狀態:`source_policy_ready_runtime_not_applied`
> 真相邊界本表把「已證明現況」、「建議目的地」、「目前阻擋」、「source risk」與「receipt 強度」分開。建議不等於 live routealias 不是 numeric chat ID本變更沒有送 Telegram、讀 token/chat ID、改 runtime route 或部署。
## 判讀規則
- `visible label` 只描述使用者畫面中的名稱;`proven runtime alias` 必須另有 source/runtime receipt兩者不得互相替代。
- `proven route` 只描述目前可由 source 或 durable receipt 證明的行為,不把畫面名稱或建議當成 live delivery。
- `recommended route` 是 canonical 產品責任分流;若 bot alias、chat alias 或 durable receipt 未證明egress 仍是 `blocked`
- `receipt strength``strong` 代表 durable DB receipt`weak` 代表 console/source 行為;`none` 代表沒有可驗證的 delivery receipt。
- 未知 product/signal/severity/destination 一律 no-egress不再沿用 ADR-093 的 unknown-to-shared-group fallback。
- `AwoooI SRE 戰情室` 只收 shared infrastructure、security、DR 與 P0/P1 incident lifecycle。raw P2/P3、marketing、成功 heartbeat、未驗證 recommendation 和 bot 對話均不得進入。
## 監控與告警來源總帳
| source truth | 數量 | 角色與目前判定 |
|---|---:|---|
| `ops/monitoring/alerts-unified.yml` | 23 groups / 145 alert declarations | production primary Prometheus rule source產品訊號必須帶 namespaced canonical route labels |
| `ops/monitoring/alerts.yml` | 81 declarations | legacy duplicate source不加入 production primary 總數;保留相同產品 labels 防止誤復用 |
| 11 個 specialized Prometheus / SignOz rule files | 112 declarations | K3s、DB、flywheel、MinIO/Kali、NVIDIA、Ollama、SLO、agent latency、SignOz 等專項訊號 |
| primary + specialized 合計 | 257 declarations / 226 unique alert names | 29 個 alert name 在兩個以上來源重複extra declarations 31重複不是多送授權仍由 grouping/fingerprint/route policy 收斂 |
| AWOOOI API `alert_rules` | 29 matching/action rules | 用於分類與修復候選,不是新的 signal generator不重複計入 257 |
| Telegram egress scanner | 805 files / 50 normalized gateway callsites | active direct Bot API 07 個 direct calls 全在 GitHub-frozen legacy source禁止執行 |
目前可明確歸戶的產品告警如下;未列出的 shared infrastructure/security/DR/K3s/DB/host 規則屬 `awoooi`,但只有 P0/P1 allowlisted lifecycle 能進 SRE 群:
| product_id | 明確 signal / alert | canonical signal family | 現行 egress |
|---|---|---|---|
| `2026fifa` | repository 中沒有產品專屬 Prometheus rule | `data_freshness_and_recommendation` | blocked |
| `agent-bounty-protocol` | registry 有 A2A、traffic security、treasury/onboarding 3 lanes沒有專屬 Prometheus rule | 對應 3 個 registry families | blocked缺 durable receipt |
| `awooogo` | 無產品專屬 rule | `*` | disabled |
| `awoooi` | 145 primary + 112 specialized declarations 的 shared/core owner | shared infrastructure / security / DR / incident lifecycle | 僅 P0/P1 allowlisted route allowed |
| `bitan-pharmacy` | `BitanWoooWorkDown` | `container_and_product_health` | blocked |
| `clawbot-openclaw` | `OpenClawDown` | `raw_monitoring_alert` | blocked不得借用 AWOOOI TYPE-3 |
| `momo-pro` | `MoWoooWorkDown``MomoScraperSuccessLow` | scheduler/sales、crawler/data freshness | blockedconfigured destination 仍 `chat_not_found` |
| `stockplatform-v2` | `StockWoooWorkDown` | `market_data_and_recommendation_freshness` | blocked |
| `tsenyang-website` | `TsenyangWebsiteDown` | `public_site_and_agent_action_health` | blocked |
| `vibework` | 無專屬 rule`VibeAIAgent` 不等於 standalone VibeWork | `*` | not implemented |
| `vtuber` | 無產品專屬 rule | `*` | disabled |
## Source-side route corrections
- Alertmanager 產品規則現在帶 `awoooi_product_id``awoooi_signal_family``awoooi_route_severity`gateway 以這組明確 context 覆蓋 legacy `TYPE-3`,所以 blocked product 不會再落到 shared SRE。
- SignOz public receiver 尚無 source authentication因此即使 payload 自稱 canonical labels 也固定 no-egressSentrysignature verified與 HMAC generic webhook 缺完整 namespaced context 時一律 `UNKNOWN / blocked_no_egress`。Alertmanager 另要求 direct peer 與完整 forwarded chain 都是 private IP不再信任單一可偽造的 forwarded value。
- `docker-compose`、production ConfigMap、docker-health scripts 與 7 個 Gitea workflows 的 11 個 raw destination defaults 已移除runtime 只可由既有 secret/env alias 注入,缺值即 fail closed。白名單也不再內嵌 numeric default。
- 本次只改 source policy沒有讀 token、呼叫 Bot API、改 Telegram 群組、送測試訊息或偽造 delivery receipt。
## 11 產品路由清冊
| 產品 | 目前 proven route | 建議 canonical route | blocked unknown / egress status | source risk | receipt strength |
|---|---|---|---|---|---|
| `2026fifa` | source-only monitoring surface沒有 runtime Telegram binding | 建立專屬 product alias 後,只送 data freshness、recommendation build、public API failure | `blocked`chat/bot alias 與 durable receipt 均未證明 | generic env 可能誤入其他產品 lane | `none` |
| `agent-bounty-protocol` | `VibeAIAgent` bot-to-group route 已證明,涵蓋 A2A/traffic/treasury/onboardingdurable receipt 仍缺 | `vibe_ai_agent_control_plane`,先收斂到 gateway 與 durable receipt 再開 egress | `blocked`route 存在但 console/source log 不能代替 durable delivery receipt | 訊息標題仍可能把 Agent Bounty 與 standalone VibeWork 混為同一產品 | `weak`console/source only |
| `awooogo` | 沒有 runtime Telegram bindingegress disabled | 維持 disabled未來啟用需先建立獨立 product alias/receipt | `disabled` | 不得用 shared SRE 當缺設定 fallback | `none` |
| `awoooi` | `AwoooI SRE 戰情室` 與 TsenYang monitoring-owner receipt 已證明;`小O_小龍蝦`/`叫小賀` 是 visible label/source-config candidate不當成 runtime alias proof | TsenYang bot 才能送 canonical shared infra/security/DR/P0-P1 lifecycleOpenClaw/NemoTron 只可在原訊息 interaction context 下回覆 | shared allowlist `allowed`raw P2/P3、business/FinOps、marketing `blocked` | 若未驗 sender bot ownershipAI bot 會繞過 canonical alert owner | `strong`(僅 shared monitoring owner |
| `bitan-pharmacy` | source-only product/container signals沒有 canonical runtime Telegram binding | 建立 Bitan-owned alias 後才允許 product health/failure | `blocked`目的地、bot alias、receipt backend 未證明 | cross-product container signal 容易被誤當 AWOOOI shared infra | `none` |
| `clawbot-openclaw` | 目前使用 `tsenyangbot` private lane形成跨產品 bot ownership drift | 建立 ClawBot-owned bot/chat bindinginteractive reply 與 monitoring 分離 | `blocked`:現有 private route 不具 ClawBot canonical ownership | 共用 AWOOOI bot 會造成 polling ownership、回覆與告警歸戶混亂 | `none` |
| `momo-pro` | MOMO bot liveconfigured chat readback 回 `400 chat_not_found` | 修復 MOMO-owned destination 後,才承接 crawler/freshness、scheduler/sales pipeline | `blocked`runtime destination unreachable且無 durable receipt | `WOOO` 只是 private bot lane/inferred不是已證明 MOMO product group | `none` |
| `stockplatform-v2` | 與 TSENYANG Website 共用 `wooowooowooobot` private lane | 建立 StockPlatform-owned destination只送 market data freshness、pipeline/API failure | `blocked`:跨產品 shared-private ownership drift無 durable receipt | 同一 private bot lane 無法清楚歸戶、抑噪與回寫 | `none` |
| `tsenyang-website` | 與 StockPlatform 共用 `wooowooowooobot` private lane | 建立 TSENYANG Website-owned destinationoperator interaction 不承接 bulk raw alert | `blocked`:跨產品 shared-private ownership drift無 durable receipt | site 與 market-data 告警混流owner/verifier 不可辨 | `none` |
| `vibework` | standalone product 沒有 runtime Telegram binding`VibeAIAgent` 屬 Agent Bounty | 保持 `not_implemented`;若實作需獨立 product_id、alias 與 receipt | `not_implemented` | 名稱相似會造成錯誤歸戶與重複告警 | `none` |
| `vtuber` | 沒有 runtime Telegram bindingegress disabled | 維持 disabled未來啟用須先建立 VTuber-owned alias | `disabled` | shared monitor 辨識到 signal 不等於產品已有 sender | `none` |
## 群組與 BOT 的責任邊界
| visible label | proven runtime alias | canonical candidate | 可以承接 | 不可以承接 |
|---|---|---|---|---|
| AwoooI SRE 戰情室 | `awoooi_sre_war_room` | TsenYang monitoring owner | shared infra/security/DR、P0/P1 lifecycle | raw P2/P3、business/FinOps、marketing、成功 heartbeat、各產品 bulk warning |
| 小O_小龍蝦 | `none`visible label only | `openclaw_bot` source-config candidate | 只在原 inbound message/thread context 下做診斷回覆 | canonical production alert ownership、root alert fanout |
| 叫小賀 | `none`source-config only | `nemotron_bot` candidate | 只在原 inbound message/thread context 下做 challenger/shadow reply | canonical production alert ownership |
| TsenYang private | `none`visible label only | `wooowooowooobot_private` candidate | 無;僅保留 ownership drift evidence | 在 ownership reconciliation 前不得視為任何產品 canonical route |
| tsenyangbot private | `none`source-config only | `tsenyangbot_private` candidate | 無;目前 ClawBot traffic 只作 drift evidence | 跨產品 monitoring ownership |
| WOOO | `none`visible label only | `wooo_private_bot_lane` candidate | 尚未證明任何 product-group ownership | 不得當作 MOMO 或 eWoooc canonical product group |
| VibeAIAgent | `none`source-config only | `vibe_ai_agent_control_plane` candidate | Agent Bounty A2A/traffic/treasury/onboarding補 durable receipt 後) | standalone VibeWork 或其他產品告警 |
| MOMO configured chat | `none`configured unreachable | `momo_configured_chat_unresolved` candidate | 無;目前 `chat_not_found` | 在 runtime readback、receipt 與 verifier 通過前不得送 |
## Source risk 與落地缺口
1. ADR-093 的 unknown/default-to-group 與全產品治理 v2 衝突legacy `resolve_chat_ids` 現在必須先通過 canonical registry。unknown、`TYPE-1` info/success 與 `TYPE-6B` business 全部回空 route不再進 shared SRE。
2. Agent Bounty 的 bot-to-`VibeAIAgent` route 已證明,但 direct Bot API 與 console log 仍不是 durable receipt因此 route 存在、egress 仍 blocked 兩件事必須同時呈現。
3. MOMO bot live 不等於 destination healthy目前 `chat_not_found` 是明確 blocked runtime truth。`WOOO` 也只能標為 private/inferred lane不能升格成 product group。
4. StockPlatform/TSENYANG Website 共用 private botClawBot 又使用 AWOOOI-owned bot都是 ownership drift需各自 owner、gateway、receipt、verifier 後才能開 route。
5. Bitan、2026FIFA 是 source-onlyAwoooGo、VibeWork、VTuber 是 disabled/not-implemented/no binding。它們不得因缺專屬群而 fallback 到 shared SRE。
6. Gateway 現在會在 provider call 前驗 product/signal/severity、destination alias 與 sender bot alias非監控的 inbound command/callback/thread reply 使用獨立 interaction classifier不與監控 allowlist 混用。
## Machine-readable truth
- Registry`docs/security/telegram-canonical-routing-registry.snapshot.json`
- Schema`docs/schemas/telegram_canonical_routing_registry_v1.schema.json`
- Resolver`apps/api/src/services/notification_matrix.py`
- Tests`apps/api/tests/test_telegram_canonical_routing_registry.py`
Production apply 前仍需同一 route change run 留下 source diff、check-mode、bounded apply、durable delivery receipt、post-verifier、rollback/no-write terminal 與 learning/writeback本 source-only 交付不代表 runtime route 已更動。

View File

@@ -0,0 +1,522 @@
{
"schema_version": "telegram_canonical_routing_registry_v1",
"generated_at": "2026-07-14T15:50:04+08:00",
"status": "source_policy_ready_runtime_not_applied",
"mode": "source_only_no_secret_no_telegram_send_no_route_change",
"supersedes": {
"policy_ref": "docs/adr/ADR-093-telegram-group-migration.md",
"superseded_behavior": "unknown_or_cross_product_alert_defaults_to_shared_sre_group",
"superseded_by": "global_product_governance_v2",
"owner": "sre_shared_runtime",
"expiry": "2026-07-14T14:35:26+08:00",
"replacement": "this canonical registry and notification_matrix fail-closed resolver",
"verifier": "apps/api/tests/test_telegram_canonical_routing_registry.py"
},
"policy": {
"default_decision": "block",
"unknown_product_decision": "block",
"unknown_signal_family_decision": "block",
"unknown_severity_decision": "block",
"unknown_destination_decision": "block",
"legacy_runtime_resolver": "resolve_chat_ids_must_pass_canonical_registry",
"legacy_shared_sre_allowed_types": [
"TYPE-2",
"TYPE-3",
"TYPE-4",
"TYPE-4D",
"TYPE-5S",
"TYPE-7E",
"TYPE-8M"
],
"legacy_shared_sre_blocked_types": ["TYPE-1", "TYPE-6B", "UNKNOWN"],
"raw_chat_id_allowed": false,
"shared_sre_rule": "Only shared infrastructure, security incident, disaster recovery, and P0/P1 incident lifecycle signals may use the AwoooI SRE war room alias.",
"shared_sre_forbidden": [
"raw product P2/P3 monitoring",
"marketing",
"success heartbeat",
"unverified recommendation",
"bot conversation"
]
},
"destination_roles": [
{
"visible_label": "AwoooI SRE 戰情室",
"proven_runtime_alias": "awoooi_sre_war_room",
"runtime_alias_evidence": "proven_runtime_receipt",
"chat_alias": "awoooi_sre_war_room",
"bot_alias": "tsenyang_bot",
"bot_owner": "sre_shared_runtime",
"monitoring_owner": true,
"role": "shared infrastructure, security, DR, and P0/P1 incident lifecycle",
"monitoring_egress_policy": "allowlist_only",
"runtime_identifier_in_registry": false
},
{
"visible_label": "TsenYang",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "visible_label_only",
"chat_alias": "wooowooowooobot_private",
"bot_alias": "wooowooowooobot",
"bot_owner": "unverified",
"monitoring_owner": false,
"role": "visible private label only; StockPlatform and TSENYANG Website runtime alias ownership is not proven",
"monitoring_egress_policy": "blocked_cross_product_ownership_drift",
"runtime_identifier_in_registry": false
},
{
"visible_label": "小O_小龍蝦",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "visible_label_only",
"chat_alias": "awoooi_sre_war_room",
"bot_alias": "openclaw_bot",
"bot_owner": "ai_agent_runtime",
"monitoring_owner": false,
"role": "visible group label mapped to a source-configured OpenClaw reply candidate; runtime alias is not proven by this label",
"monitoring_egress_policy": "interaction_reply_only_no_monitoring_ownership",
"runtime_identifier_in_registry": false
},
{
"visible_label": "叫小賀",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "source_config_only",
"chat_alias": "awoooi_sre_war_room",
"bot_alias": "nemotron_bot",
"bot_owner": "ai_agent_runtime",
"monitoring_owner": false,
"role": "source-configured NemoTron reply candidate; visible label alone is not runtime proof",
"monitoring_egress_policy": "shadow_reply_only_no_production_alert_ownership",
"runtime_identifier_in_registry": false
},
{
"visible_label": "WOOO",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "visible_label_only",
"chat_alias": "wooo_private_bot_lane",
"bot_alias": "none",
"bot_owner": "unverified",
"monitoring_owner": false,
"role": "observed private bot lane; product-group ownership is inferred and not proven",
"monitoring_egress_policy": "blocked_not_a_proven_product_group",
"runtime_identifier_in_registry": false
},
{
"visible_label": "VibeAIAgent",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "source_config_only",
"chat_alias": "vibe_ai_agent_control_plane",
"bot_alias": "vibe_ai_agent_bot",
"bot_owner": "agent_bounty_product_ops",
"monitoring_owner": false,
"role": "proven Agent Bounty bot-to-group route for A2A task, traffic, treasury, and onboarding signals",
"monitoring_egress_policy": "blocked_until_durable_receipt_backend_is_verified",
"runtime_identifier_in_registry": false
},
{
"visible_label": "tsenyangbot private",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "source_config_only",
"chat_alias": "tsenyangbot_private",
"bot_alias": "tsenyang_bot",
"bot_owner": "sre_shared_runtime",
"monitoring_owner": false,
"role": "observed ClawBot private lane using an AWOOOI-owned bot binding",
"monitoring_egress_policy": "blocked_cross_product_bot_ownership_drift",
"runtime_identifier_in_registry": false
},
{
"visible_label": "MOMO configured chat",
"proven_runtime_alias": "none",
"runtime_alias_evidence": "configured_unreachable",
"chat_alias": "momo_configured_chat_unresolved",
"bot_alias": "momo_bot",
"bot_owner": "momo_product_ops",
"monitoring_owner": false,
"role": "MOMO bot is live but the configured destination currently returns chat_not_found",
"monitoring_egress_policy": "blocked_runtime_destination_unreachable",
"runtime_identifier_in_registry": false
}
],
"products": [
{
"product_id": "2026fifa",
"display_name": "2026 FIFA World Cup",
"owner": "2026fifa_product_ops",
"source_audit_status": "source_only_no_runtime_telegram_binding",
"egress_status": "blocked"
},
{
"product_id": "agent-bounty-protocol",
"display_name": "Agent Bounty Protocol",
"owner": "agent_bounty_product_ops",
"source_audit_status": "vibe_ai_agent_bot_to_group_route_proven_durable_receipt_gap",
"egress_status": "blocked"
},
{
"product_id": "awooogo",
"display_name": "AwoooGo",
"owner": "awooogo_product_ops",
"source_audit_status": "no_runtime_telegram_binding_egress_disabled",
"egress_status": "disabled"
},
{
"product_id": "awoooi",
"display_name": "AWOOOI / IWOOOS",
"owner": "sre_shared_runtime",
"source_audit_status": "shared_group_and_tsenyang_monitoring_owner_receipt_proven_openclaw_nemotron_labels_not_runtime_proof",
"egress_status": "allowed"
},
{
"product_id": "bitan-pharmacy",
"display_name": "Bitan Pharmacy",
"owner": "bitan_product_ops",
"source_audit_status": "source_only_no_canonical_runtime_telegram_binding",
"egress_status": "blocked"
},
{
"product_id": "clawbot-openclaw",
"display_name": "Clawbot / OpenClaw",
"owner": "ai_agent_runtime",
"source_audit_status": "tsenyangbot_private_route_observed_cross_product_bot_ownership_drift",
"egress_status": "blocked"
},
{
"product_id": "momo-pro",
"display_name": "MOMO PRO / eWoooc",
"owner": "momo_product_ops",
"source_audit_status": "momo_bot_live_configured_chat_returns_chat_not_found",
"egress_status": "blocked"
},
{
"product_id": "stockplatform-v2",
"display_name": "StockPlatform v2",
"owner": "stockplatform_product_ops",
"source_audit_status": "wooowooowooobot_private_route_shared_with_tsenyang_site_ownership_drift",
"egress_status": "blocked"
},
{
"product_id": "tsenyang-website",
"display_name": "TSENYANG Website",
"owner": "tsenyang_site_ops",
"source_audit_status": "wooowooowooobot_private_route_shared_with_stockplatform_ownership_drift",
"egress_status": "blocked"
},
{
"product_id": "vibework",
"display_name": "VibeWork",
"owner": "vibework_product_ops",
"source_audit_status": "no_runtime_telegram_binding_agent_bounty_is_separate_owner",
"egress_status": "not_implemented"
},
{
"product_id": "vtuber",
"display_name": "VTuber",
"owner": "vtuber_product_ops",
"source_audit_status": "no_runtime_telegram_binding_egress_disabled",
"egress_status": "disabled"
}
],
"routes": [
{
"route_id": "2026fifa-product-monitoring",
"product_id": "2026fifa",
"signal_family": "data_freshness_and_recommendation",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "unassigned",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "2026fifa_product_ops",
"egress_status": "blocked",
"blocked_reason": "canonical_product_destination_and_durable_receipt_backend_missing"
},
{
"route_id": "agent-bounty-a2a-task-lifecycle",
"product_id": "agent-bounty-protocol",
"signal_family": "a2a_task_lifecycle",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "vibe_ai_agent_bot",
"chat_alias": "vibe_ai_agent_control_plane",
"allowed_destination": "blocked",
"receipt_backend": "console_log_only_unverified",
"owner": "agent_bounty_product_ops",
"egress_status": "blocked",
"blocked_reason": "direct_bot_api_and_durable_delivery_receipt_gap"
},
{
"route_id": "agent-bounty-traffic-security",
"product_id": "agent-bounty-protocol",
"signal_family": "traffic_security",
"severity": ["P0", "P1", "P2"],
"scope": "product",
"bot_alias": "vibe_ai_agent_bot",
"chat_alias": "vibe_ai_agent_control_plane",
"allowed_destination": "blocked",
"receipt_backend": "console_log_only_unverified",
"owner": "agent_bounty_product_ops",
"egress_status": "blocked",
"blocked_reason": "direct_bot_api_and_durable_delivery_receipt_gap"
},
{
"route_id": "agent-bounty-treasury-onboarding",
"product_id": "agent-bounty-protocol",
"signal_family": "treasury_and_onboarding",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "vibe_ai_agent_bot",
"chat_alias": "vibe_ai_agent_control_plane",
"allowed_destination": "blocked",
"receipt_backend": "console_log_only_unverified",
"owner": "agent_bounty_product_ops",
"egress_status": "blocked",
"blocked_reason": "direct_bot_api_and_durable_delivery_receipt_gap"
},
{
"route_id": "awooogo-disabled",
"product_id": "awooogo",
"signal_family": "*",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "none",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "awooogo_product_ops",
"egress_status": "disabled",
"blocked_reason": "telegram_egress_disabled"
},
{
"route_id": "awoooi-shared-infrastructure",
"product_id": "awoooi",
"signal_family": "shared_infrastructure",
"severity": ["P0", "P1"],
"scope": "shared",
"bot_alias": "tsenyang_bot",
"chat_alias": "awoooi_sre_war_room",
"allowed_destination": "telegram_chat_alias:awoooi_sre_war_room",
"receipt_backend": "awoooi.alert_operation_log+telegram_outbound_receipts",
"owner": "sre_shared_runtime",
"egress_status": "allowed"
},
{
"route_id": "awoooi-security-incident",
"product_id": "awoooi",
"signal_family": "security_incident",
"severity": ["P0", "P1"],
"scope": "shared",
"bot_alias": "tsenyang_bot",
"chat_alias": "awoooi_sre_war_room",
"allowed_destination": "telegram_chat_alias:awoooi_sre_war_room",
"receipt_backend": "awoooi.alert_operation_log+telegram_outbound_receipts",
"owner": "security_runtime",
"egress_status": "allowed"
},
{
"route_id": "awoooi-disaster-recovery",
"product_id": "awoooi",
"signal_family": "disaster_recovery",
"severity": ["P0", "P1"],
"scope": "shared",
"bot_alias": "tsenyang_bot",
"chat_alias": "awoooi_sre_war_room",
"allowed_destination": "telegram_chat_alias:awoooi_sre_war_room",
"receipt_backend": "awoooi.alert_operation_log+telegram_outbound_receipts",
"owner": "dr_runtime",
"egress_status": "allowed"
},
{
"route_id": "awoooi-incident-lifecycle",
"product_id": "awoooi",
"signal_family": "incident_lifecycle",
"severity": ["P0", "P1"],
"scope": "shared",
"bot_alias": "tsenyang_bot",
"chat_alias": "awoooi_sre_war_room",
"allowed_destination": "telegram_chat_alias:awoooi_sre_war_room",
"receipt_backend": "awoooi.alert_operation_log+telegram_outbound_receipts",
"owner": "sre_shared_runtime",
"egress_status": "allowed"
},
{
"route_id": "awoooi-raw-product-monitoring",
"product_id": "awoooi",
"signal_family": "product_raw_monitoring",
"severity": ["P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "none",
"allowed_destination": "blocked",
"receipt_backend": "awoooi.alert_operation_log",
"owner": "product_route_registry",
"egress_status": "blocked",
"blocked_reason": "raw_p2_p3_must_not_use_shared_sre_destination"
},
{
"route_id": "awoooi-marketing",
"product_id": "awoooi",
"signal_family": "marketing",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "none",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "product_route_registry",
"egress_status": "blocked",
"blocked_reason": "marketing_must_not_use_monitoring_egress"
},
{
"route_id": "awoooi-business-finops",
"product_id": "awoooi",
"signal_family": "business_finops",
"severity": [
"P0",
"P1",
"P2",
"P3"
],
"scope": "product",
"bot_alias": "none",
"chat_alias": "none",
"allowed_destination": "blocked",
"receipt_backend": "awoooi.alert_operation_log",
"owner": "finops_runtime",
"egress_status": "blocked",
"blocked_reason": "business_and_cost_signals_require_a_separate_owned_destination"
},
{
"route_id": "bitan-product-health",
"product_id": "bitan-pharmacy",
"signal_family": "container_and_product_health",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "unassigned",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "bitan_product_ops",
"egress_status": "blocked",
"blocked_reason": "current_shared_sre_fanout_is_not_a_canonical_product_route"
},
{
"route_id": "clawbot-raw-monitoring",
"product_id": "clawbot-openclaw",
"signal_family": "raw_monitoring_alert",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "tsenyang_bot",
"chat_alias": "tsenyangbot_private",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "ai_agent_runtime",
"egress_status": "blocked",
"blocked_reason": "cross_product_bot_ownership_drift_requires_a_clawbot_owned_binding"
},
{
"route_id": "momo-data-freshness",
"product_id": "momo-pro",
"signal_family": "crawler_and_data_freshness",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "momo_bot",
"chat_alias": "momo_configured_chat_unresolved",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "momo_product_ops",
"egress_status": "blocked",
"blocked_reason": "configured_destination_returns_chat_not_found_and_has_no_durable_receipt"
},
{
"route_id": "momo-scheduler-sales",
"product_id": "momo-pro",
"signal_family": "scheduler_and_sales_pipeline",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "momo_bot",
"chat_alias": "momo_configured_chat_unresolved",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "momo_product_ops",
"egress_status": "blocked",
"blocked_reason": "configured_destination_returns_chat_not_found_and_has_no_durable_receipt"
},
{
"route_id": "stockplatform-market-data",
"product_id": "stockplatform-v2",
"signal_family": "market_data_and_recommendation_freshness",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "wooowooowooobot",
"chat_alias": "wooowooowooobot_private",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "stockplatform_product_ops",
"egress_status": "blocked",
"blocked_reason": "private_route_is_shared_with_tsenyang_site_and_product_ownership_is_not_reconciled"
},
{
"route_id": "tsenyang-site-health",
"product_id": "tsenyang-website",
"signal_family": "public_site_and_agent_action_health",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "wooowooowooobot",
"chat_alias": "wooowooowooobot_private",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "tsenyang_site_ops",
"egress_status": "blocked",
"blocked_reason": "private_route_is_shared_with_stockplatform_and_product_ownership_is_not_reconciled"
},
{
"route_id": "vibework-not-implemented",
"product_id": "vibework",
"signal_family": "*",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "none",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "vibework_product_ops",
"egress_status": "not_implemented",
"blocked_reason": "standalone_vibework_has_no_telegram_egress_agent_bounty_is_separate"
},
{
"route_id": "vtuber-disabled",
"product_id": "vtuber",
"signal_family": "*",
"severity": ["P0", "P1", "P2", "P3"],
"scope": "product",
"bot_alias": "none",
"chat_alias": "none",
"allowed_destination": "blocked",
"receipt_backend": "none",
"owner": "vtuber_product_ops",
"egress_status": "disabled",
"blocked_reason": "telegram_egress_disabled"
}
],
"rollups": {
"product_count": 11,
"route_count": 20,
"allowed_route_count": 4,
"blocked_route_count": 13,
"disabled_route_count": 2,
"not_implemented_route_count": 1,
"shared_sre_allowed_route_count": 4,
"raw_numeric_destination_count": 0,
"runtime_route_change_count": 0,
"telegram_send_count": 0
},
"execution_boundaries": {
"telegram_send_allowed": false,
"bot_api_call_allowed": false,
"secret_value_read_allowed": false,
"raw_chat_id_storage_allowed": false,
"runtime_route_change_allowed": false,
"production_deploy_allowed": false
}
}