Sonde qui vérifie la livraison, pas la disponibilité
`GET /api/health/contact` répond 200 si un lead peut atteindre un humain maintenant et 503 sinon. Il vérifie deux choses séparément : que le jeton du canal de notification est toujours valide (une vraie requête au fournisseur, avec un timeout de 8 secondes) et que le journal des requêtes est scriptable. La réponse n’est composée que de valeurs logiques — jamais le jeton, l’identifiant du canal ou le nom du bot. Le résultat est conservé en mémoire 60 secondes, afin qu’une sonde externe sollicitée souvent ne devienne pas elle-même du trafic. Lorsque le fournisseur est inaccessible, le champ devient `null`, pas `false` : « je ne sais pas » et « invalide » sont des états différents.
Journal écrit avant la livraison, pas après
Chaque requête reçoit un identifiant et est écrite dans un journal append-only, une ligne JSON par événement, *avant* que la notification ne soit tentée. La deuxième ligne, avec le même identifiant, indique ce qui s’est réellement passé : `delivered` ou `failed`, avec le motif tronqué à 300 caractères. Le dossier est créé avec les droits `0700`, les fichiers avec `0600`, et le chemin est volontairement placé en dehors du répertoire de version, afin que l’historique survive à une publication.
Vérification d’acceptation exécutée après publication
Un script de vérification ouvre chaque page produit, par groupes de trois, et échoue s’il manque le code 200, l’ancre des questions, l’ancre d’exemple ou l’adresse canonique. Il vérifie ensuite la carte du site — qu’elle ne contienne pas de variantes de langue qui ne peuvent pas s’ouvrir et qu’elle contienne chaque produit — ainsi que `robots.txt`, afin de ne pas bloquer les ressources nécessaires au rendu. Il échoue aussi si le contenu d’un ancien client réapparaît dans la page. C’est une liste de choses qui ont déjà cassé une fois.
Mises à jour avec gate, pas avec l’espoir
Le script de publication refuse de démarrer si le serveur a moins de 500 MB de mémoire libre, exécute `npm audit --audit-level=high` et s’arrête sur les vulnérabilités de niveau élevé si vous ne confirmez pas explicitement, puis construit à partir de zéro — `.next` et `node_modules` supprimés, installation à partir du fichier de verrouillage. Si le processus n’apparaît pas `online` après le démarrage, le script affiche les 50 dernières lignes du journal et quitte avec une erreur, au lieu de signaler un succès.
Redémarrage avec des seuils, pas au hasard
Le processus redémarre automatiquement au-dessus de 500 MB de mémoire, avec un délai de 4 secondes entre les tentatives, une augmentation exponentielle du délai et un arrêt après 10 redémarrages instables — afin qu’une boucle de plantage devienne visible au lieu de consommer le serveur en silence. Délai de grâce à l’arrêt : 5 secondes, puis arrêt forcé. Les journaux ont une date et un fuseau horaire, dans un seul flux.
Doublons traités, pas comptés deux fois
La même personne, le même message, deux fois — un double clic, un rechargement de la page — produisait deux notifications identiques pour une seule demande. Désormais, l’empreinte `sha256` de la paire adresse + message est conservée 10 minutes en mémoire ; la deuxième soumission reçoit le même identifiant de demande et le marquage `duplicate`, et la notification n’est pas répétée. Seuls les doublons d’une livraison réussie sont supprimés ; une livraison échouée a le droit de passer à nouveau.
Des codes d’erreur qui disent ce qui s’est cassé
400 pour des données invalides, 405 pour une mauvaise méthode, 500 pour un corps de requête corrompu, 502 quand le canal de livraison a mal répondu, 503 quand les identifiants manquent. La différence compte à 3 heures du matin : 502 signifie « le fournisseur », 503 signifie « notre configuration ». Le visiteur reçoit distinctement « nous avons reçu la demande, mais nous n’avons pas pu notifier » — pas un faux succès.
Rétention appliquée par le code, pas promise dans la politique
La note de confidentialité publiée indique que les demandes du formulaire sont conservées 24 mois. Le code applique exactement le même nombre : `LEAD_RETENTION_DAYS` par défaut 730, et les fichiers plus anciens que le seuil sont supprimés à chaque écriture, sans planificateur qui puisse être oublié. L’adresse IP est tronquée au préfixe de réseau — `/24` en IPv4, `/48` en IPv6 — et l’on lit le dernier saut dans l’en-tête, pas le premier, parce que le premier est envoyé par le client.
Nous trouvons aussi ce que personne n’a signalé
Le même passage dans le code a mis au jour deux choses qu’aucun utilisateur n’avait signalées : la limite de 10 requêtes par minute pouvait être complètement contournée, parce que le premier élément de l’en-tête des adresses redirigées était lu — celui contrôlé par le client — et une route d’initialisation des appels téléphoniques était ouverte anonymement, aux frais et depuis notre numéro, bien que le composant qui l’utilisait ne soit plus monté nulle part. Les deux ont été corrigées et vérifiées en production.