T6 Où vit le code

Par où commencer quand on reprend ce projet.
Lire T7 avant de modifier quoi que ce soit

Le moteur embarqué ressemble à des outils connus sans se comporter comme eux. Ses pièges sont silencieux et ne se devinent pas à la lecture. T7 · Pièges du runtime

Le flux est la seule source de vérité

Il n'y a pasde fichiers .lua sur la passerelle
Il y aun document JSON unique, où chaque fonction est embarquée dans un node
On le récupèrepar l'API du moteur, sur le port 8080 de la passerelle
Attentionle flux en service peut contenir des nodes absents de votre copie
Toujours repartir du flux en service

Un capteur déclaré depuis le cockpit ajoute un node au flux de la passerelle. Envoyer un instantané plus ancien le supprime sans rien signaler. C'est arrivé le 29 août, et a failli se reproduire le 4 septembre. Le contrôle préalable compare désormais les deux et refuse tout envoi qui retirerait un node.

Un dépôt, deux fichiers par site

Tout ce qui fait fonctionner la passerelle vit dans un seul dépôt : le flux éclaté, l'interface du cockpit, les tests, les scripts de déploiement et cette documentation. Devis, spécifications et comptes rendus vivent ailleurs.

Le dépôt ne contient que la structure, identique d'une passerelle à l'autre. Ce qui appartient à un site donné tient dans deux fichiers qui ne sont jamais versionnés :

FichierContenuModèle versionné
config/<site>.env Mots de passe, clés d'API, URL de supervision config/exemple.env
config/<site>.balises.json Capteurs déclarés, avec leur clé racine LoRa config/balises.modele.json

Dans le code des fonctions, une valeur secrète est un marqueur {{NOM}}, remplacé à l'assemblage par la valeur du site. Les capteurs sont ajoutés au flux au même moment. Sans fichier de balises, la passerelle se déploie sans aucun capteur : c'est l'état voulu d'une passerelle neuve.

Pourquoi cette séparation

Une clé racine LoRa permet d'usurper le capteur et de déchiffrer son trafic. Elle appartient au client, pas au dépôt — et les capteurs d'un client n'ont rien à faire sur la passerelle d'un autre.

Les tests refusent tout flux où une clé réapparaîtrait dans le squelette, parce qu'un dépôt ne se nettoie pas après coup : ce qui y entre une fois y reste.

Lire une fonction

Le flux fait plusieurs centaines de milliers de caractères. Un outil en extrait une fonction sous forme de fichier Lua autonome :

python3 tests/extract_node.py build_iobase_msg /tmp/f.lua

C'est aussi ce qui rend les tests possibles : la fonction extraite est enveloppée pour être appelable avec des bouchons, sans passerelle.

Les tests

bash tests/run.sh

Tout tourne sur un poste ordinaire, en quelques secondes :

== syntaxe des fonctions du flow ==
67 fonctions, 0 erreur(s)

== routage des mesures par environnement ==
3. balise pointee sur B, B CONFIGURE
  ✅ part vers l hote B
  ✅ avec le jeton de B, pas celui de A
  ✅ journalise env=b
…
16 ok, 0 ko
SuiteCe qu'elle protège
SyntaxeUne erreur de syntaxe n'apparaîtrait qu'à l'exécution du node, donc peut-être jamais
Routage par environnementUne mesure ne doit jamais partir avec le jeton d'un autre environnement
Cycle d'authentificationLe plus critique : jetons à usage unique, verrous, restauration
CockpitCe qu'on écrit dans l'interface doit être ce qui route réellement
Downlink et rotationLes deux nodes qui portaient le motif interdit
StatistiquesElles vivent à part de l'état de service et ne doivent pas l'abîmer
CâblageCe qu'aucune lecture de node ne montre — voir ci-dessous
Le contrôle de câblage rattrape le plus

Il cherche des liaisons vers un node inexistant, un champ obligatoire manquant, un motif interdit, et un fichier écrit mais jamais relu. Ce dernier contrôle aurait attrapé le défaut qui aurait fait perdre à un environnement son jeton à chaque redémarrage.

Ajouter un test

  1. Extraire les nodes concernés
  2. Écrire les cas dans un fichier Lua
  3. Le déclarer dans le script de lancement
Décrire l'incident que le test protège, en tête de fichier Un test dont on ignore le pourquoi finit par être supprimé
Vérifier qu'il échoue quand on réintroduit le défaut Un contrôle qui ne détecte rien est pire qu'aucun contrôle
Des tests rouges deux semaines n'ont alerté personne

Après un changement d'architecture, les tests décrivaient un fonctionnement abandonné. Ils échouaient, personne ne les relisait. Même famille que le code jamais exécuté : un signal que personne ne regarde ne protège de rien.

Vérifier que la documentation dit encore vrai

SITE=mon-site python3 scripts/verifie_doc.py

Les pages affirment des faits sur la passerelle : une adresse d'usine, un port qui écoute, un fichier qui existe ou qui n'existe pas. Ces affirmations ne sont pas recopiées dans un script, elles sont portées par les pages elles-mêmes, en attribut data-verif, et confrontées à une vraie passerelle.

<code data-verif="usine:network:lan_eth0.ipaddr=192.168.1.1">192.168.1.1</code>

Cinq formes, et cinq seulement : port:, fichier:, absent:, usine: qui lit l'image d'origine sous /rom, et config: qui lit la configuration en service. Ce qui n'y rentre pas n'est pas balisé, garde son étiquette d'incertitude dans la page, et personne ne le prend pour un fait établi.

Pourquoi ce contrôle existe

Le 18/09, cette documentation annonçait 192.168.42.1 comme adresse d'usine. C'est 192.168.1.1, et une passerelle neuve distribue des adresses alors que celle en service n'en distribue aucune. La phrase avait été héritée d'une version antérieure, elle concordait avec la passerelle configurée, et personne ne l'avait rouverte. Un technicien devant une CloudGate neuve serait resté devant un écran vide, à la première action du premier chapitre.

C'est le même fil que les cinq incidents recensés en T8 : non pas du code faux, mais quelque chose qu'on croyait vrai et que rien ne contredisait. D'où la règle appliquée ici comme ailleurs, préférer ce qui lit le réel à ce qui le décrit.

Modifier le flux

Éditer le JSON à la main est risqué. La pratique du projet est un script qui charge le document, remplace un fragment en vérifiant que l'ancre est unique, et réécrit.

Ancre uniqueDeux occurrences signalent qu'on modifie autre chose que ce qu'on croit
Même sérialisationUne différence d'encodage produit un diff illisible de centaines de milliers de caractères
Comparer document à documentVérifier après coup que seuls les nodes visés ont changé

Ensuite

T7 · Pièges du runtime Dix comportements qui ne se devinent pas. À lire avant de modifier.
T8 · Décisions et dette Pourquoi c'est fait ainsi. À lire avant de vouloir simplifier.
T1 · Architecture Les chaînes, le stockage, la résilience.
E4 · Diagnostiquer Contient la séquence de déploiement.