T6 Où vit le code
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 pas | de fichiers .lua sur la passerelle |
|---|---|
| Il y a | un document JSON unique, où chaque fonction est embarquée dans un node |
| On le récupère | par l'API du moteur, sur le port 8080 de la passerelle |
| Attention | le flux en service peut contenir des nodes absents de votre copie |
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 :
| Fichier | Contenu | Modè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.
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
| Suite | Ce qu'elle protège |
|---|---|
| Syntaxe | Une erreur de syntaxe n'apparaîtrait qu'à l'exécution du node, donc peut-être jamais |
| Routage par environnement | Une mesure ne doit jamais partir avec le jeton d'un autre environnement |
| Cycle d'authentification | Le plus critique : jetons à usage unique, verrous, restauration |
| Cockpit | Ce qu'on écrit dans l'interface doit être ce qui route réellement |
| Downlink et rotation | Les deux nodes qui portaient le motif interdit |
| Statistiques | Elles vivent à part de l'état de service et ne doivent pas l'abîmer |
| Câblage | Ce qu'aucune lecture de node ne montre — voir ci-dessous |
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
- Extraire les nodes concernés
- Écrire les cas dans un fichier Lua
- 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 |
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.
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 unique | Deux occurrences signalent qu'on modifie autre chose que ce qu'on croit |
| Même sérialisation | Une différence d'encodage produit un diff illisible de centaines de milliers de caractères |
| Comparer document à document | Vé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. |