Contrat de surveillance
Deux types de heartbeat. Cette page couvre la surveillance d’une extension Chrome enregistrée : votre agent envoie son rapport à POST /api/v1/monitoring/report. Les environnements d’un projet web utilisent dans le panneau un endpoint distinct, POST /api/v1/heartbeat/report, avec un autre jeton. Le rapport d’environnement minimal est {"overall":"ok"} ; un payload de métriques personnalisées v2 est aussi accepté. N’intervertissez pas les deux endpoints en configurant un agent. La section 8b décrit le heartbeat d’un environnement.
La surveillance place votre extension sous observation constante : un feu tricolore en direct sur sa carte dans la liste, un onglet de base monitoring dans la vue Détail et un message de statut Telegram distinct avec des alertes de changement d’état. Elle combine deux composants activables indépendamment : un heartbeat push (un agent sur l’hôte de votre service envoie ses rapports selon une planification déclarée ; le silence est lui-même un signal) et pings de disponibilité (le panneau vérifie la disponibilité de vos URL publiques).
1. Fonctionnement (deux composants, un seul feu)
- Heartbeat push : un script cron sur votre hôte (par exemple un
monitor.shadapté) envoie par POST un rapport de métriques v2 au panneau à l’intervalle que vous avez déclaré. Chaque rapport doit contenir le verdict réservéoverall. Les rapports manqués font monter le feu en gravité (dispositif de l’homme mort) : si l’hôte, le réseau ou cron tombe, vous le voyez justement parce que les appels cessent. - Pings de disponibilité : le panneau interroge lui-même périodiquement les URL publiques que vous configurez (site, api, docs) et ne vérifie que la disponibilité, jamais le contenu. Aucun agent n’est nécessaire, une URL suffit.
Une extension est « sous surveillance » lorsque la surveillance est activée et qu’au moins un composant est configuré. Sinon, sa carte affiche un point gris neutre.
1b. Heartbeats et réserve d’emplacements
La surveillance se situe au niveau de l’extension, sous forme de deux listes plates indépendantes qui partagent une même réserve d’emplacements : heartbeats (producteurs en mode push, anciennement « stands ») et
pings de disponibilité (vérifications en mode pull). Une extension peut avoir plusieurs heartbeats, par exemple un hôte principal et un worker payant. Chaque heartbeat envoie ses rapports avec son propre jeton bearer et déclare sa propre présentation (onglets, sections et lignes Telegram) via son label. Les pings de disponibilité forment une liste plate distincte, rattachée à l’extension et non imbriquée sous un heartbeat. Le panneau fusionne le tout en un seul feu agrégé (le pire état), une seule surface Détail et un seul message Telegram récapitulatif.
Les heartbeats et les pings d’une extension partagent une seule réserve d’emplacements : un heartbeat consomme un emplacement, un ping actif consomme un emplacement, et leur somme ne doit pas dépasser le max_slots du forfait (Essai 3 · Studio 10 · Fleet 25). Dépasser la réserve renvoie 409 slot_limit ; après un passage à un forfait inférieur, l’excédent est suspendu (les pings d’abord, les heartbeats en dernier). La réponse contient slots_used et max_slots pour que l’interface puisse afficher le budget.
2. Le feu tricolore
L’agrégat est le pire état calculé sur le composant de chaque heartbeat activé et le feu de chaque ping actif, les gravités étant ordonnées ainsi : ok < warn < crit. Le composant d’un heartbeat vaut max(reported, watchdog) : reported est la pire gravité entre la métrique overall et les seuils dépassés (6.2) du dernier rapport accepté ; watchdog est un plancher de fraîcheur calculé à partir de l’horloge de réception du panneau (un décalage de l’horloge de l’agent est sans effet).
| État | Couleur | Quand |
|---|---|---|
off | ⚪ gris | Surveillance non activée ou aucun composant configuré. |
paused | ⚪ gris | Activé mais gelé (limite du forfait ou pause manuelle). Les rapports restent stockés. |
green | 🟢 | Tous les composants ok. |
yellow | 🟡 | Pire composant warn. |
red | 🔴 | Pire composant crit. |
La gravité canonique est ok | warn | crit. Les alias usuels sont normalisés à la réception : warning devient warn ; error et critical deviennent crit.
3. Watchdog (heartbeats manqués)
Vous déclarez l’intervalle de rapport attendu (60 s à 24 h) en activant le heartbeat. Le watchdog impose ensuite au feu un plancher selon la fraîcheur, avec grace = max(60s, 25% of the interval) pour absorber la gigue du cron et du réseau :
| Âge du dernier rapport accepté | Plancher du watchdog |
|---|---|
| jusqu’à intervalle + délai de grâce | ok (vert) |
| 1 rapport manqué (jusqu’à 2 × intervalle + délai de grâce) | warn (jaune) |
| 2 rapports manqués ou plus | crit (rouge) |
Un rapport rejeté (401 / 413 / 422 / 429) ne rafraîchit jamais le watchdog : un agent défaillant fait donc honnêtement monter la gravité ; le dernier motif de rejet est visible dans les paramètres de surveillance. Le rétablissement est immédiat : le premier rapport accepté recalcule aussitôt le feu.
4. Pings de disponibilité
Un ping est une vérification de disponibilité nommée d’une URL https publique. Le panneau effectue un GET avec un délai d’expiration de 10 s, suit au plus 5 redirections, ne lit pas le corps au-delà d’une limite minimale et considère un statut final de classe 2xx comme un succès (204 et 206 comptent aussi). Un 3xx final (boucle ou limite de redirections), un 4xx, un 5xx, un délai dépassé ou une erreur DNS/TLS/réseau est un échec. Une vérification en échec est confirmée par une nouvelle tentative après ~15 s avant d’être comptée, ce qui absorbe les coupures réseau ponctuelles.
| Échecs consécutifs confirmés | Feu du ping |
|---|---|
| 0 | ok |
| 1 | warn |
| 2 ou plus | crit |
Les résultats des pings apparaissent comme métriques natives du panneau ping:<name> dans la section
pings (Uptime) de l’onglet de base
monitoring et comme ligne Telegram réservée pings : 🟢 api 210ms · 🟢 site 90ms · 🔴 docs (timeout). Le nombre de pings par extension et l’intervalle minimal dépendent du forfait :
| Forfait | Pings de disponibilité par extension | Intervalle minimal de ping |
|---|---|---|
| Essai | 3 | 5 min |
| Indie | 3 | 5 min |
| Studio | 10 | 1 min |
| Fleet | 25 | 1 min |
La disponibilité de la surveillance et le nombre d’emplacements dépendent de votre forfait ; l’intervalle minimal de heartbeat est de 60 s.
5. Mettre une extension sous surveillance
Dans l’extension, section Paramètres, bloc de surveillance :
- Activez la surveillance si votre forfait l’inclut.
- Heartbeat : activez-le et déclarez l’intervalle des rapports ; le panneau émet un jeton de surveillance (bearer), affiché une seule fois (vous pouvez le renouveler à tout moment ; l’ancien jeton reçoit immédiatement 401). Placez le jeton et l’URL de l’endpoint dans l’environnement de votre agent.
- Pings : ajoutez des URL publiques nommées (par exemple
site,api,docs) avec un intervalle propre à chaque ping, dans la limite du forfait. - Telegram : activez l’envoi des messages de surveillance et, si besoin, définissez un groupe/chat distinct.
6. Le format du rapport
Le rapport est le même payload v2 piloté par les métadonnées que celui du
contrat de métriques personnalisées (onglets, sections, lignes Telegram, métriques), envoyé vers POST /api/v1/monitoring/report avec Authorization: Bearer <monitoring token>. La surveillance ajoute des identifiants réservés :
| Identifiant réservé | Statut | Signification |
|---|---|---|
overall | obligatoire | Métrique de type status : le verdict de l’exécution, gravité ok | warn | crit. Alimente l’agrégat. Un rapport sans overall valide est rejeté (422). |
issues | recommandé | Métrique de type list : les problèmes en cours, items au niveau supérieur de la métrique. Jusqu’à 5 éléments sont cités dans les textes d’alerte. |
ping:* | interdit | Espace de noms des identifiants des métriques de ping propres au panneau ; tout envoi est rejeté (422). |
pings | interdit | Identifiant de ligne Telegram de la ligne ping du panneau ; tout envoi est rejeté (422). |
monitoring | onglet appartenant au panneau | Identifiant de l’onglet de base. Les métriques peuvent y faire référence (dashboard.tab: "monitoring") sans le déclarer dans tabs ; une déclaration est ignorée. |
base | onglet appartenant au panneau | L’id de l’onglet Basic, réservé de la même façon : une déclaration est ignorée. |
Limites : payload d’au plus 64 Ko, au plus 200 métriques, au plus 2 rapports par minute par source (l’excédent reçoit 429 et ne compte pas comme un heartbeat).
Un corps de plus de 64 Ko reçoit 413 payload_too_large et n’est jamais tronqué ni lu comme un rapport plus court. Un rapport qui enfreint les règles ci-dessus reçoit 422 invalid_report avec la liste de tous les problèmes. Les deux motifs sont conservés comme dernière erreur du heartbeat :
HTTP/1.1 413 Payload Too Large
{ "code": "payload_too_large", "message": "report is over 64 KB; send fewer metrics or split them across heartbeats",
"limit_bytes": 65536, "size_bytes": 71680 }
HTTP/1.1 422 Unprocessable Entity
{ "code": "invalid_report", "message": "report rejected",
"errors": [ "metric \"overall\" must have kind \"status\" (got \"text\")",
"metric id \"ping:x\" uses the reserved \"ping:\" namespace" ] }6.1. Exemple de rapport
{
"schema_version": "2.0",
"generated_at": "2026-07-12T10:19:58Z",
"tabs": [ { "id": "stand", "title": "Stand", "order": 5, "icon": "server" } ],
"sections": [ { "tab": "stand", "id": "docker", "title": "Docker", "order": 0 },
{ "tab": "monitoring", "id": "host", "title": "Host", "order": 10 } ],
"telegram": { "header": "{sev} {name} monitoring",
"lines": [ { "id": "host", "order": 0, "prefix": "🖥 " },
{ "id": "docker", "order": 1, "prefix": "📦 " } ] },
"metrics": [
{ "id": "overall", "kind": "status", "state": "ok", "severity": "ok" },
{ "id": "issues", "kind": "list",
"items": [ { "text": "RAM 91% (7.1/7.8G)", "severity": "warn" } ],
"dashboard": { "show": true, "tab": "monitoring", "section": "host", "order": 0,
"render": "list", "name": "Issues" },
"telegram": { "show": false } },
{ "id": "ram", "kind": "percent", "label": "RAM", "value": 91, "severity": "warn",
"dashboard": { "show": true, "tab": "monitoring", "section": "host", "order": 1,
"render": "kpi" },
"telegram": { "show": true, "line": "host", "pos": 10,
"format": "RAM {value}%", "sep": " · " } }
]
}Réponse réussie :
{ "accepted": true, "effective": "green", "next_expected_by": "2026-07-12T10:26:13Z" }6.2. Seuils sur les métriques du rapport
Toute métrique numérique d'un rapport, sauf overall, peut porter thresholds (le bloc du contrat des métriques personnalisées, section 5.4). Le panneau calcule la sévérité d'une telle métrique à la réception, et le voyant du heartbeat est le pire entre overall et les seuils dépassés : une métrique au-dessus de crit rend le heartbeat rouge même quand overall vaut ok. recover se compare au rapport accepté précédent du même heartbeat. Un bloc mal formé reçoit 422 invalid_report avec la raison dans la liste. Un rapport sans seuils se comporte comme avant.
{ "id": "queue", "kind": "number", "label": "Mail queue", "value": 41,
"thresholds": { "direction": "above", "warn": 20, "crit": 100, "unit": "msgs", "recover": 15 },
"dashboard": { "show": true, "tab": "monitoring", "section": "host", "order": 2, "render": "kpi" },
"telegram": { "show": true, "line": "host", "pos": 20, "format": "queue {value}" } }Le panneau l'affiche comme 41 (threshold 20/100 msgs) sur une tuile jaune, et le heartbeat est jaune.
7. Intégrer un script de surveillance (style monitor.sh)
Si vous exécutez déjà un script cron de contrôle de santé, ajoutez à la fin de chaque exécution un POST sans garantie de livraison (best-effort). Un échec d’envoi ne doit pas interrompre l’exécution : le watchdog détectera de toute façon un silence prolongé.
# monitor.env
EXTOPS_REPORT_URL="https://extops.dev/api/v1/monitoring/report"
EXTOPS_REPORT_TOKEN="<monitoring token from Settings>"
# end of the tick: build the payload with jq from the values you already computed,
# then push it best-effort (never let delivery break the tick itself)
payload="$(jq -n --arg sev "$SEVERITY" '{
schema_version: "2.0",
generated_at: (now | todate),
metrics: [ { id: "overall", kind: "status", state: $sev, severity: $sev } ]
}')"
code="$(curl -sS -m 10 -o /tmp/extops-report.json -w '%{http_code}' -X POST "$EXTOPS_REPORT_URL" \
-H "Authorization: Bearer $EXTOPS_REPORT_TOKEN" \
-H "Content-Type: application/json" \
-d "$payload" || echo 000)"
# 413 payload_too_large: over 64 KB; 422 invalid_report: .errors lists every problem
case "$code" in 413|422) logger -t monitor "panel: $code $(jq -c . /tmp/extops-report.json)" ;; esac
Faites correspondre les gravités de votre script à l’ensemble canonique (critical est accepté et normalisé en crit) et alimentez la métrique issues avec votre liste de problèmes existante. Réglez l’intervalle déclaré sur la période du cron.
8. Telegram : message d’état et alertes séparés
- Les données de surveillance sont envoyées dans Telegram sous forme de message de statut distinct et modifiable, séparé du statut des métriques personnalisées : en-tête + la ligne réservée
pings+ les lignes déclarées par votre rapport. - Si besoin, envoyez la surveillance vers un groupe/chat distinct ; sinon, c’est le chat du canal de l’extension qui est utilisé. Le transport du bot est partagé avec le canal de l’extension.
- Alertes de changement d’état se déclenchent à chaque changement de la couleur agrégée, dans les deux sens, y compris le retour au vert (avec la durée de l’incident). Tant que la couleur est stable, rien n’est envoyé ; les modifications de configuration (pause, modification des pings) ne déclenchent pas d’alerte.
8b. Heartbeats des projets web
Un projet web a ses propres heartbeats. Chacun appartient à un environnement (par exemple Production) et possède son propre jeton hb_. Les règles du rapport sont celles de la section 6 ; voici ce qui diffère.
POST /api/v1/heartbeat/report
Authorization: Bearer hb_<64 hex>
Content-Type: application/json
{"overall":"ok"}- Corps : un rapport v2 avec la métrique obligatoire
overallde type status, ou le corps minimal{"overall":"ok"}avecok,warnoucrit. Le panneau transforme le corps minimal en rapport v2 et enregistre le corps normalisé. - Limites : au plus 64 Ko par rapport, au plus 4 rapports par minute par jeton et au plus 200 métriques. Un rapport de plus de 200 métriques reçoit 422, comme pour une extension.
- Réponses :
200 {"accepted":true,"light":"green"},401pour un jeton inconnu,413 payload_too_large,422 invalid_report,429avecRetry-After: 60. Les motifs des 413 et 422 s’affichent comme Last error dans Setup, Monitoring. - Covers : un heartbeat couvre zéro ou plusieurs surfaces de son environnement. Son feu colore chaque surface couverte ; un heartbeat qui n’en couvre aucune colore l’environnement. Covers se règle dans le panneau, pas dans le rapport.
- Clair : vert jusqu’à interval + grace après le dernier rapport accepté, jaune jusqu’à 2 × interval + grace, rouge ensuite, avec
grace = max(60s, 25% of the interval). L’intervalle va de 60s à 24h. - Waiting for first report : un nouveau heartbeat reste gris jusqu’à son premier rapport accepté. Après 24 heures sans rapport, il passe au jaune et envoie une notification ; il n’est jamais rouge avant son premier rapport.
- Seuils : comme dans la section 6.2. Le voyant de la réponse 200 et du panneau est le pire entre la fraîcheur,
overallet les seuils dépassés, et par Covers il atteint les surfaces couvertes. - Onglets : les métriques avec
tab:"monitoring"apparaissent sur le premier onglet de la section Monitoring du projet, etbaseest aussi réservé. Les onglets que vous déclarez deviennent des onglets à part.
8c. Qui se connecte à vos services et comment
Un projet web peut ajouter des vérifications de ports à un Service dont l'adresse est un hôte, par exemple un serveur mail. Ces vérifications sont faites par Ext Ops Panel lui-même. Cette section dit qui se connecte, ce qui est envoyé, à quelle fréquence et comment un hôte peut refuser. Le contrat d'agent ci-dessus ne change pas.
- D'où : une seule adresse,
89.117.144.124, avec le nomprobe.extops.dev. Le nom pointe vers cette adresse, et l'adresse pointe en retour vers le nom. Il n'y a pas d'autre point d'observation. - Uniquement des hôtes vérifiés : une vérification ne tourne que sur un hôte sous un domaine que le propriétaire du projet a vérifié par un enregistrement DNS TXT ou un fichier well-known. Si la preuve disparaît, les vérifications se mettent en pause.
- Ports : TCP 25, 110, 143, 465, 587, 993, 995 ; TLS 443, 465, 993, 995 ; SMTP 25 et 587 ; SMTPS 465 ; IMAP 143 ; IMAPS 993. Aucun autre port n'est jamais vérifié.
- TCP et TLS : se connecter et lire l'accueil envoyé par le serveur, ou terminer une négociation TLS et lire le certificat. Rien n'est envoyé.
- SMTP : attendre l'accueil
220, envoyerEHLO probe.extops.dev, envoyerSTARTTLSsi le serveur le propose et terminer la négociation, puis envoyerQUIT. SMTPS sur le port 465 fait de même à l'intérieur de TLS, sans STARTTLS. - IMAP : attendre
* OK, envoyera1 CAPABILITY, envoyerSTARTTLSsur le port 143 s'il est proposé, envoyerID ("name" "ExtOpsPanel-Probe" "support-url" "https://extops.dev/docs/monitoring#probes")seulement si le serveur annonce ID, puis envoyerLOGOUT. IMAPS sur le port 993 fait de même à l'intérieur de TLS. - Jamais envoyé :
AUTH,LOGIN,AUTHENTICATE,MAIL FROM,RCPT TO,DATA,VRFY,EXPN,SELECT. La sonde ne se connecte jamais à un compte, n'envoie jamais de mail et n'ouvre jamais de boîte. Chaque ligne passe une liste fixe de commandes autorisées avant d'être écrite, et un test échoue si une autre commande atteint un serveur. - À quelle fréquence : une vérification mail tourne au plus toutes les 5 minutes, quel que soit le forfait, avec un écart de 10 % dans les deux sens. Un hôte reçoit au plus une connexion à la fois et au plus 40 connexions par heure, pour tous les projets ensemble. Au-delà, l'hôte est laissé tranquille jusqu'à l'heure suivante.
- Quand un serveur refuse : un SMTP
421arrête toutes les vérifications de cet hôte pendant une heure. Un SMTP554ou un IMAP* BYEest enregistré comme un refus et n'est retenté qu'à l'intervalle suivant. - Limites d'une vérification : 5 secondes pour se connecter, 10 secondes pour tout le dialogue, au plus 4 Ko lus.
- Ce qui est conservé : le résultat, le code de réponse, la latence, la première ligne de l'accueil coupée à 120 caractères sans aucune adresse e-mail, une courte liste de capacités comme STARTTLS et IMAP4rev1, et les dates du certificat. Ni l'accueil complet, ni les réponses à EHLO ou ID, ni aucune adresse.
- Refuser les vérifications : si vous exploitez un hôte et ne voulez pas de ces vérifications, écrivez à
support@extops.dev avec le nom de l'hôte. Nous excluons l'hôte ou tout son domaine, et plus rien ne s'y connecte ensuite. Vous pouvez aussi répondre
421ou bloquer89.117.144.124. Le propriétaire du projet peut mettre en pause ou supprimer une vérification à tout moment dans Setup, Monitoring.
9. Le contrat lisible par machine
La description formelle de l’endpoint de rapport, du schéma du payload, des identifiants réservés et de la sémantique du feu tricolore se trouve dans le fichier OpenAPI 3.1. Il porte la même version de contrat (2.1.0).