Aller au contenu

#Sonde de santé

GET /api/sante dit si la plateforme fonctionne — et le dit en la faisant travailler, pas en constatant qu'un serveur répond.

C'est la seule route publique de l'API : elle n'attend ni clé, ni session, ni en-tête. Un moniteur externe n'a rien de tout cela, et c'est justement ce qui fait de lui une surveillance externe.

#Ce qu'elle mesure vraiment

Vérifier qu'une adresse rend 200 ne prouve qu'une chose : qu'un serveur a servi du HTML. Si la base est tombée, l'espace client répond toujours 200 — avec un écran vide — et un moniteur naïf annonce que tout va bien pendant que plus personne ne peut se connecter ni publier.

Une sonde qui ne peut pas échouer ne mesure rien. Celle-ci exerce cinq choses, chacune par un appel réel :

CléNomCe qui est exercé
baseBase de donnéesUne lecture effective, avec le rôle public
connexionConnexionLe service d'authentification
renduRendu des sitesLe moteur de thèmes, et la justesse de sa sortie
siteSites clientsUn site publié, de bout en bout, feuille de style comprise
ecranÉcran de connexionLa page d'entrée du CMS, rendue et hydratable

Les trois premières interrogent des dépendances depuis l'intérieur. Les deux dernières sortent et rentrent par la porte d'entrée, exactement comme un visiteur — et c'est la seule façon de voir une page qui répond parfaitement et ne marche pas.

Le troisième mérite un mot. Il ne se contente pas d'appeler le moteur : il compare le résultat de asset_url à la valeur attendue. Ce filtre a produit pendant des semaines un segment de chemin en trop, et la conséquence était qu'aucun site sur thème Liquid ne chargeait sa feuille de style. Rien ne levait, rien n'était en erreur, tous les moniteurs étaient au vert : les pages s'affichaient, simplement sans aucun style. Un contrôle « le moteur a répondu » aurait été vert ce jour-là aussi.

#La réponse

GET /api/santejson
{
  "etat": "ok",
  "mesureLe": "2026-08-06T14:13:08.172Z",
  "composants": [
    { "cle": "base", "nom": "Base de données", "etat": "ok", "ms": 265 },
    { "cle": "connexion", "nom": "Connexion", "etat": "ok", "ms": 146 },
    { "cle": "rendu", "nom": "Rendu des sites", "etat": "ok", "ms": 30 },
    { "cle": "site", "nom": "Sites clients", "etat": "ok", "ms": 463 },
    { "cle": "ecran", "nom": "Écran de connexion", "etat": "ok", "ms": 333 }
  ]
}

etat vaut ok ou hs. Le code HTTP porte la même information — 200 quand tout répond, 503 dès qu'un composant manque — parce que c'est la seule chose que lisent la plupart des moniteurs.

ms est la durée de la sonde, pas celle de la requête entière.

#Un composant à la fois

?c=<clé> isole un composant et fait porter le code HTTP par lui seul.

GET /api/sante?c=basejson
{
  "composant": "base",
  "nom": "Base de données",
  "etat": "ok",
  "ms": 265,
  "mesureLe": "2026-08-06T14:13:08.172Z"
}

Une clé inconnue rend 404 avec { "error": "composant_inconnu" }.

Ce paramètre existe pour une raison pratique : la plupart des outils de surveillance ne savent qu'appeler une URL et regarder son code de retour. Une adresse unique ne peut donc donner qu'un seul voyant — et « base de données » et « connexion » fondues dans un seul voyant ne disent pas laquelle des deux est tombée. Une adresse par composant, et chacun obtient sa propre courbe.

C'est ainsi qu'est bâtie la page de statut de Webcosa.

#La panne qu'un code 200 ne montre pas

Le 6 août, le site vitrine a passé des heures sans qu'un seul clic soit possible et sans qu'une seule animation tourne. Pendant ce temps : 200, en 40 ms, HTML complet et juste. Tous les moniteurs étaient au vert, et ils avaient raison — ils mesurent qu'un serveur répond. Les trois sondes de dépendances étaient vertes aussi, et elles avaient raison également : la base, l'authentification et le moteur fonctionnaient.

La panne ne vivait dans aucune dépendance. Elle vivait dans le contrat entre un en-tête et un corps : la politique de sécurité exigeait un nonce que le HTML mis en cache ne portait pas. Le navigateur bloquait donc tous les scripts en ligne — ceux où Next met le flux RSC et l'amorce d'hydratation — et React ne s'attachait jamais.

site et ecran vérifient précisément ce contrat, en plus du reste :

  • la page répond, et son corps n'est pas vide ;
  • si sa politique réclame un nonce, le HTML le porte réellement ;
  • pour un site client, la feuille de style du thème est présente : posée dans la page et non vide, ou allée chercher à l'adresse écrite dans la page, et elle répond.

Ce dernier point couvre l'autre panne silencieuse de l'histoire du produit : asset_url a produit pendant des semaines un segment de chemin en trop, et plus aucun site sur thème Liquid ne chargeait son CSS. La sonde rendu vérifie ce filtre en mémoire ; site va jusqu'au fichier. Un chemin juste dont le fichier manque — thème dépublié, politique de lecture resserrée, stockage en panne — ne se voit que par là.

#Le site témoin

site sonde un site publié désigné par SANTE_SITE_TEMOIN, et non « le premier site venu ». Le jour où ce client-là met son site en brouillon ou casse son thème, la page de statut annoncerait « Sites clients : indisponible » pour une panne qui n'existe pas. Un témoin surveille la plateforme : il ne doit dépendre que de nous. Sans témoin configuré, la sonde n'apparaît pas dans le rapport plutôt que d'être rouge en permanence.

#Ce qu'elles ne vérifient pas

Qu'on a reçu le site demandé. Ce serait la sentinelle idéale contre une fuite entre clients par le cache, mais aucun marqueur d'identité n'est engendré par le rendu public — ni lien canonique, ni og:url. Le seul repère disponible est le contenu éditorial, qu'un client change quand il veut, et une sonde qui vire au rouge parce qu'un artisan a réécrit son titre ne serait pas une sonde.

#Ce qu'elle ne dit pas

Volontairement : aucun identifiant, aucun nom de site, aucun compte, et aucun message d'erreur brut. Un message de pilote de base de données nomme volontiers un hôte ou un rôle ; chaque sonde ne rend donc qu'un état et une durée.

Ce qu'elle divulgue est exactement ce qu'une page de statut publie de toute façon : est-ce que ça marche.

#Fréquence

La réponse est mémorisée dix secondes. Deux conséquences à connaître avant de bâtir dessus :

  • interroger plus souvent ne donne rien de plus frais ;
  • une route publique qui interroge la base à chaque appel serait un amplificateur — quelques boucles suffiraient à lui faire porter la charge.

Une cadence d'une minute est un bon réglage. En deçà, on mesure la mémoire.

Attention

Cette mémoire n'est pas partagée, et ce point mérite d'être dit. Elle vit dans une variable de module, donc dans une instance de fonction. La montée en charge en démarre plusieurs, et chacune repart à zéro et rejoue les cinq sondes. Le plafond réel est donc de six requêtes par minute et par instance, pas six en tout.

Elle amortit les appels répétés d'un même moniteur — ce pour quoi elle a été écrite. Elle ne borne pas ce qu'un trafic concurrent peut déclencher. Ne bâtis pas dessus l'hypothèse d'une charge plafonnée.

#Cache et CORS

La réponse porte Cache-Control: no-store : une réponse mise en cache dirait l'état d'il y a dix minutes à qui vient savoir si le service répond maintenant.

Aucun en-tête Access-Control-Allow-Origin n'est émis. Les moniteurs ne sont pas des navigateurs et n'en ont pas besoin ; l'ouvrir laisserait n'importe quelle page du web faire sonder la base par ses visiteurs.