Kanäle-Übersicht
Ongrid spricht mit Menschen über Kanäle. Ein Kanal ist entweder:
- ein Benachrichtigungskanal — Ongrid pusht Alarme an eine Chat-Oberfläche; der Mensch liest, aber der Kanal selbst trägt nie eine Antwort zurück zum Agenten. Das sind
notify_channelsin der DB und dieinternal/pkg/notify/webhook.go-Sender. - ein IM-Kanal — Ongrid sitzt als Bot auf der Chat-Oberfläche eines Workspaces, liest eingehende Nachrichten und führt dieselbe Agent-Logik aus, die die Web-UI ausführt, und streamt die Antwort als Edits einer Platzhalter-Nachricht zurück. Das sind
im_appsin der DB undinternal/manager/biz/imbridge/provider/*.
Die beiden sind unabhängig. Telegram-Alarme zu verdrahten (sendMessage out) lässt Benutzer nicht mit dem Agenten auf Telegram chatten, und umgekehrt.
Andere Tabelle, andere Credentials, gleiche UI
Beide Oberflächen werden in Settings → Channels in der Web-UI konfiguriert. Benachrichtigungskanäle gehen in den Notify-Tab; IM-Kanäle in den IM bridge-Tab.
Welchen will ich?
| Ziel | Verwenden | Datei |
|---|---|---|
| Gefeuerte Alarme an einen Slack-Channel pushen | Slack-Incoming-Webhook (Notify) | internal/pkg/notify/webhook.go NewSlackSender |
| Gefeuerte Alarme an Feishu/Lark-Gruppe pushen | Feishu Custom Bot (Notify) | NewFeishuSender |
| Gefeuerte Alarme an DingTalk-Gruppe pushen | DingTalk Custom Bot (Notify) | NewDingTalkSender |
| Gefeuerte Alarme an WeCom-Gruppe pushen | WeCom Group Bot (Notify) | NewWeComSender |
| Generisches JSON-POST an eigenen Dienst | Webhook (Notify) | NewGenericWebhookSender |
| Telegram-Alarme (einseitig) | Telegram-Bot sendMessage (Notify) | NewTelegramSender |
| Mit dem Agenten von Slack aus als Bot reden | Slack Socket Mode App (IM bridge) | imbridge/provider/slack/ |
| Mit dem Agenten von Telegram aus reden | Telegram-Bot getUpdates (IM bridge) | imbridge/provider/telegram/ |
| Mit dem Agenten von Feishu/Larksuite aus reden | Feishu Long-Connection (IM bridge) | imbridge/provider/feishu/ |
Ein Workspace kann beides betreiben: z. B. einen Slack-Incoming-Webhook für Alarme plus eine separate Slack Socket Mode App für Konversation. Sie teilen keinen Zustand.
Benachrichtigungskanäle
Ein Benachrichtigungskanal ist ein zustandsloser Send(ctx, Message)-Sender. Er wird von der Alarmpipeline aufgerufen, wann immer ein Alarm feuert (oder sich erholt, oder das Dämpfungsfenster abläuft). Alle Kanaltypen rendern dieselbe kanonische notify.Message-Form; nur das Payload-Format und das Signaturprotokoll unterscheiden sich pro Provider.
Gemeinsame Felder, die ein Sender erhält
type Message struct {
Severity Severity // critical | warning | info
Subject string // alert rule name + target
Body string // human-readable detail
Source string // "alert" | "test" | ...
Labels map[string]string // rule, incident_id, device_id, ...
DedupeKey string // pipeline:rule:label-set
OccurredAt time.Time
}Der Slack-Sender rendert dies in das Attachments-Format mit einer severity-getönten Farbschiene, strukturierten Feldern (Severity, Source, Rule, Incident, Device, Dedupe) und einem ts-Footer. Die Feishu- / DingTalk- / WeCom-Sender flachen es zu einem [SEV] subject\nbody\nsource:…\ndedupe:… Textpayload, weil ihre Bot-APIs in v1 nur reinen Text liefern.
Signaturmodelle auf einen Blick
| Provider | Wie er authentifiziert |
|---|---|
| Slack | Die Webhook-URL selbst ist das Secret. Keine weitere Signatur. |
| Feishu | HMAC-SHA256(ts\nsecret), platziert im JSON-Body als sign. |
| DingTalk | HMAC-SHA256(ts\nsecret), platziert im URL-Query als sign. |
| WeCom | Bot-Key im URL-Query. Keine weitere Signatur. |
| Telegram | Bot-Token im Pfad (/bot<TOKEN>/sendMessage). |
| Webhook | Optionaler X-Ongrid-Signature: sha256=<HMAC> über den Body. |
Die exakten Implementierungen leben in internal/pkg/notify/webhook.go: signFeishu, signDingTalkURL, signGenericWebhook.
Slack droppt das secret-Feld stillschweigend
Die Slack-Incoming-Webhook-URL ist selbst die Credential. Wenn Sie das Secret-Feld für einen Slack-Kanal ausfüllen, droppt der Channel-Builder es, bevor er den Sender konstruiert. Das ist beabsichtigt — Slacks Protokoll hat keine separate Signaturoberfläche für Incoming Webhooks.
IM-Kanäle
Ein IM-Kanal ist eine langlaufende Goroutine, die:
- Ausgehend zum Provider verbindet (WebSocket für Feishu/Slack, Long-Poll für Telegram). Keine eingehenden Ports werden auf dem Manager geöffnet.
- Eingehende Benutzernachrichten empfängt, durch
allow_fromfiltert und den Text anbizbridge.Bridge.HandleInboundübergibt. HandleInboundpostet eine Platzhalter-Antwort, führt den vollen Agent-Graphen aus (denselben, den die Web-UI verwendet) und streamt Edits zurück an die Platzhalter-Nachricht-ID.
Derselbe Agent-Kernel, dieselben Skills und dieselbe Persona-Registry treiben sowohl die Web-UI als auch die IM-Kanäle an. Es gibt keinen „IM-spezifischen Agenten" — der coordinator ist derselbe, den Sie auf /chat sehen.
Eingehend ist provider-spezifisch, ausgehend ist einheitlich
Provider-Unterschiede (Slack envelope_id ack, Telegram update offset, Feishu encrypt_key) leben in imbridge/provider/*/stream.go. Sobald eine Nachricht in bizbridge.HandleInbound ist, ist alles Downstream provider-agnostisch.
default_locale
Sowohl IM- als auch Notify-Zeilen tragen ein optionales default_locale. Der Validator akzeptiert den leeren String (auto), en oder zh nur — en-US / zh-CN werden zu ihrem primären Subtag gefaltet, und EN-us / ein vertipptes Locale wird vorne in AppInput.validate abgelehnt.
| Wert | Verhalten |
|---|---|
"" (auto) | Keine Direktive. Das LLM spiegelt die Sprache des Benutzers. Legacy-Default. |
en | Fügt eine Respond in English-Direktive zum System-Prompt hinzu. |
zh | Fügt eine 「请用中文回复」-Direktive zum System-Prompt hinzu. |
Das ist unabhängig vom UI-Locale (ONGRID_DEFAULT_LOCALE-Umgebungsvariable oder Browser-Sprache). Ein IM-Kanal gewinnt immer für Nachrichten, die durch ihn empfangen werden. Siehe das AI-Output-Locale-Feedback für die Auto-Trigger-Fallback-Regel auf manager-initiierten Ausgaben.
allow_from
Die Sender-Allowlist. Erforderlich für Telegram und Slack; optional für Feishu/DingTalk. Sie wird genau einmal von ParseAllowFrom geparst, geteilt zwischen Validator und jeder Provider-Poll/Stream-Loop, sodass die Parse-Regel eine einzige Definition hat.
Syntax. Komma, Leerzeichen, Newline, Tab, Semikolon — jede Kombination trennt Tokens. Reihenfolge bleibt erhalten, Duplikate werden gedroppt. Die telegram:- und tg:-Präfixe werden stillschweigend entfernt (OpenClaw-Kompatibilität).
Per-Provider-Format.
- Telegram. Nur numerische User-IDs. Nicht-numerische Tokens und negative Werte (Gruppen-Chat-IDs) werden zur Validierungszeit gedroppt. Mindestens eine ID ist erforderlich — der Bot ist öffentlich per Benutzername auffindbar, sodass eine leere Allowlist es jedem Telegram-Benutzer erlauben würde, einen tool-ausgestatteten Agenten zu kommandieren. Siehe ADR-031.
- Slack. User-IDs, die mit
Ubeginnen (oderWfür Enterprise-Gäste). Mindestens eine ist erforderlich. Finden Sie Ihre eigene, indem Sie auf Ihr Workspace-Profil klicken →⋯→ Copy member ID. - Feishu / DingTalk. Optional. Diese Plattformen sind durch Enterprise-Tenant-Mitgliedschaft gegated; nur Org-Mitglieder können den Bot erreichen.
Leere Allowlist für Telegram oder Slack wird abgelehnt
Der Validator gibt telegram requires allow_from / slack requires allow_from zurück, wenn die geparste Liste leer ist. Es gibt keinen „Deny by default, aber Setup später erlauben"-Modus. Der Bot ist öffentlich erreichbar; der Operator muss die Tür bewusst öffnen.
Failure-Mode ist stilles Droppen. Eine Nachricht von einem nicht in der Allowlist befindlichen Sender wird auf WARN mit der user_id des Senders geloggt, dann gedroppt. Keine Antwort, kein Platzhalter, kein Agent-Lauf. Der Bot bestätigt nicht einmal, dass er existiert — gleiche Form wie OpenClaw dmPolicy: allowFrom.
Wie eine gefeuerte Alarm-Zustellung aussieht
alert evaluator → produces notify.Message
↓
notify.Sender (per channel) ↓ buildBody(msg) → JSON payload
↓ signTarget(endpoint, secret, body) → headers / URL params
↓ POST endpoint
↓ resp.StatusCode in [200, 299] → success
↓
chat surfaceFür tieferes Detail siehe die Per-Kanal-Seiten: