Aperçu des canaux
Ongrid parle aux humains à travers des canaux. Un canal est soit :
- un canal de notification — Ongrid pousse les alertes vers une surface de chat ; l'humain lit, mais le canal lui-même ne porte jamais de réponse en retour vers l'agent. C'est
notify_channelsdans la DB et les Sendersinternal/pkg/notify/webhook.go. - un canal IM — Ongrid s'installe sur la surface de chat d'un workspace comme bot, lit les messages entrants et exécute le même raisonnement d'agent que l'UI web exécute, streamant la réponse en retour comme des edits à un message placeholder. C'est
im_appsdans la DB etinternal/manager/biz/imbridge/provider/*.
Les deux sont indépendants. Câbler les alertes Telegram (sendMessage sortant) ne laisse pas les utilisateurs chatter avec l'agent sur Telegram, et inversement.
Table différente, credential différent, même UI
Les deux surfaces sont configurées dans Settings → Channels dans l'UI web. Les canaux de notification vont à l'onglet Notify ; les canaux IM vont à l'onglet IM bridge.
Lequel je veux ?
| Objectif | Utilisez | Fichier |
|---|---|---|
| Pousser les alertes parties vers un canal Slack | Webhook entrant Slack (Notify) | internal/pkg/notify/webhook.go NewSlackSender |
| Pousser les alertes parties vers groupe Feishu/Lark | Bot personnalisé Feishu (Notify) | NewFeishuSender |
| Pousser les alertes parties vers groupe DingTalk | Bot personnalisé DingTalk (Notify) | NewDingTalkSender |
| Pousser les alertes parties vers groupe WeCom | Bot de groupe WeCom (Notify) | NewWeComSender |
| POST JSON générique vers votre propre service | Webhook (Notify) | NewGenericWebhookSender |
| Alertes Telegram (direction unique) | Bot Telegram sendMessage (Notify) | NewTelegramSender |
| Parler à l'agent depuis Slack comme bot | App Slack Socket Mode (pont IM) | imbridge/provider/slack/ |
| Parler à l'agent depuis Telegram | Bot Telegram getUpdates (pont IM) | imbridge/provider/telegram/ |
| Parler à l'agent depuis Feishu/Larksuite | Long-connection Feishu (pont IM) | imbridge/provider/feishu/ |
Un workspace peut exécuter les deux : par ex. un webhook entrant Slack pour les alertes plus une app Slack Socket Mode séparée pour la conversation. Ils ne partagent pas d'état.
Canaux de notification
Un canal de notification est un Sender Send(ctx, Message) sans état. Il est appelé par le pipeline d'alerte chaque fois qu'une alerte part (ou récupère, ou la fenêtre de dampening expire). Tous les types de canaux rendent la même forme canonique notify.Message ; seul le format de payload et le protocole de signature diffèrent par provider.
Champs communs qu'un Sender reçoit
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
}Le sender Slack rend ceci dans le format attachments avec un rail de couleur teinté par sévérité, des champs structurés (Severity, Source, Rule, Incident, Device, Dedupe), et un footer ts. Les senders Feishu / DingTalk / WeCom l'aplatissent vers un payload texte [SEV] subject\nbody\nsource:…\ndedupe:… parce que leurs APIs de bot ne livrent que du texte brut en v1.
Modèles de signature en un coup d'œil
| Provider | Comment il authentifie |
|---|---|
| Slack | L'URL de webhook elle-même est le secret. Pas de signature en plus. |
| Feishu | HMAC-SHA256(ts\nsecret), placé dans le corps JSON comme sign. |
| DingTalk | HMAC-SHA256(ts\nsecret), placé dans la query URL comme sign. |
| WeCom | Clé de bot dans la query URL. Pas de signature en plus. |
| Telegram | Token de bot dans le path (/bot<TOKEN>/sendMessage). |
| Webhook | Optionnel X-Ongrid-Signature: sha256=<HMAC> sur le corps. |
Les implémentations exactes vivent dans internal/pkg/notify/webhook.go : signFeishu, signDingTalkURL, signGenericWebhook.
Slack drop le champ secret silencieusement
L'URL de webhook entrant Slack est elle-même le credential. Si vous remplissez le champ Secret pour un canal Slack, le constructeur de canal le drop avant de construire le Sender. C'est intentionnel — le protocole Slack n'a pas de surface de signature séparée pour les webhooks entrants.
Canaux IM
Un canal IM est une goroutine longue durée qui :
- Se connecte en sortant vers le provider (websocket pour Feishu/Slack, long poll pour Telegram). Aucun port entrant n'est ouvert sur le manager.
- Reçoit les messages utilisateur entrants, les filtre via
allow_from, et passe le texte àbizbridge.Bridge.HandleInbound. HandleInboundposte une réponse placeholder, exécute le graphe d'agent complet (le même que l'UI web utilise), et stream les edits en retour vers l'id de message placeholder.
Le même kernel d'agent, skills et registre de personas alimentent à la fois l'UI web et les canaux IM. Il n'y a pas d'« agent spécifique à l'IM » — le coordinator est le même que vous voyez sur /chat.
L'entrant est spécifique au provider, le sortant est uniforme
Les différences de provider (ack envelope_id Slack, offset d'update Telegram, encrypt_key Feishu) vivent dans imbridge/provider/*/stream.go. Une fois un message à l'intérieur de bizbridge.HandleInbound, tout en aval est provider-agnostique.
default_locale
Les lignes IM et Notify portent toutes les deux un default_locale optionnel. Le validateur n'accepte que la chaîne vide (auto), en, ou zh — en-US / zh-CN sont pliés vers leur subtag principal, et EN-us / une locale avec typo est rejetée d'emblée dans AppInput.validate.
| Valeur | Comportement |
|---|---|
"" (auto) | Pas de directive. Le LLM mirror la langue de l'utilisateur. Défaut legacy. |
en | Ajoute une directive Respond in English au system prompt. |
zh | Ajoute une directive 「请用中文回复」 au system prompt. |
Ceci est indépendant de la locale UI (variable d'env ONGRID_DEFAULT_LOCALE ou langue du navigateur). Un canal IM gagne toujours pour les messages reçus à travers lui. Voir le feedback sur la locale de sortie IA pour la règle de fallback auto-trigger sur les sorties initiées par le manager.
allow_from
L'allowlist d'expéditeurs. Requise pour Telegram et Slack ; optionnelle pour Feishu/DingTalk. Elle est parsée exactement une fois par ParseAllowFrom, partagée entre le validateur et la boucle de poll/stream de chaque provider pour que la règle de parse ait une définition unique.
Syntaxe. Virgule, espace, nouvelle ligne, tabulation, point-virgule — toute combinaison sépare les tokens. L'ordre est préservé, les duplicatas sont droppés. Les préfixes telegram: et tg: sont retirés silencieusement (compat OpenClaw).
Format par provider.
- Telegram. IDs utilisateur numériques seulement. Les tokens non-numériques et les valeurs négatives (IDs de chat de groupe) sont droppés au validate time. Au moins un ID est requis — le bot est publiquement découvrable par username, donc une allowlist vide laisserait n'importe quel utilisateur Telegram commander un agent équipé d'outils. Voir ADR-031.
- Slack. IDs utilisateur commençant par
U(ouWpour les invités Enterprise). Au moins un est requis. Trouvez le vôtre en cliquant sur le profil de workspace →⋯→ Copy member ID. - Feishu / DingTalk. Optionnel. Ces plateformes sont gatées par appartenance au tenant entreprise ; seuls les membres de l'organisation atteignent le bot.
Une allowlist vide pour Telegram ou Slack est rejetée
Le validateur renvoie telegram requires allow_from / slack requires allow_from quand la liste parsée est vide. Il n'y a pas de mode « deny par défaut mais autoriser plus tard le setup ». Le bot est publiquement joignable ; l'opérateur doit consciemment ouvrir la porte.
Le mode d'échec est le drop silencieux. Un message d'un expéditeur non-allowlisté est loggué à WARN avec l'user_id de l'expéditeur, puis droppé. Pas de réponse, pas de placeholder, pas d'exécution d'agent. Le bot ne confirme même pas qu'il existe — la même forme que dmPolicy: allowFrom d'OpenClaw.
À quoi ressemble une livraison d'alerte partie
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 surfacePour plus de détails, voir les pages par canal :