Un système de commandement et contrôle tactique est une application distribuée fonctionnant à travers radios, véhicules, opérateurs débarqués et serveurs arrière. Le bus de messagerie en est la colonne vertébrale. Choisissez le mauvais et le système paraît rapide en laboratoire et meurt sur une liaison contestée. Choisissez le bon et l'opérateur voit une image fusionnée se rafraîchir à l'intérieur de son cycle de décision.

Cet article parcourt les quatre candidats qui apparaissent réellement dans les builds C2 de production — NATS, Apache Kafka, MQTT et RabbitMQ — et pose un cadre de décision pour choisir entre eux. La version courte : il n'y a pas de réponse unique. Les vrais systèmes en exécutent deux ou trois, pontés.

1. Le problème de la messagerie tactique

Les réseaux tactiques ne sont pas des réseaux de datacenter. La bande passante sur une radio de combat VHF typique se mesure en kilobits par seconde, pas en mégabits. Les temps aller-retour à travers un MANET (réseau ad-hoc mobile) franchissent régulièrement 500 ms. Une perte de paquets supérieure à 20 % est normale sous brouillage. Les liaisons fluctuent quand les plateformes passent derrière le terrain. Un backhaul satcom devient saturé dans la poussée du matin et au crépuscule.

L'opérateur ne tolère pas les données périmées. Une piste fusionnée vieille de deux minutes est pire qu'aucune piste — elle présente un mensonge confiant sur l'emplacement d'une menace. Le bus doit donc imposer l'expiration des messages, prioriser l'état frais sur l'arriéré, et se dégrader gracieusement quand la liaison revient après une partition plutôt que de déverser dix minutes de télémétrie en attente d'un coup.

L'ordre compte aussi, mais pas uniformément. La télémétrie peut être fusionnée (seule la dernière position compte). Les commandes non — un « weapons hold » émis à T+5 ne doit pas être dépassé par un « weapons free » émis à T+3 arrivé en retard. Le bus a besoin de sémantiques de livraison différentes par topic, pas d'une garantie globale unique.

Enfin, le bus doit survivre à la partition. Quand la liaison radio revient après une chute de cinq minutes, trois comportements sont mauvais : déverser tous les messages en attente d'un coup (submerge le consommateur), tout rejeter silencieusement (perd les commandes ordonnées) et réordonner pendant le rattrapage (livre un weapons-free périmé après un weapons-hold frais). Le comportement correct est par topic : fusion à la récupération pour la télémétrie, drain ordonné avec horodatage pour les commandes, replay complet pour les journaux d'audit. Aucun mode de livraison unique ne satisfait les trois.

2. NATS et JetStream

NATS est un petit bus pub-sub opiniâtre écrit en Go. Un binaire unique, aucune dépendance externe, sujets en mémoire par défaut, et latence publication-livraison sous-milliseconde sur un LAN. L'empreinte est de dizaines de mégaoctets — assez petite pour un calculateur de véhicule ou un nœud périphérique durci.

NATS de base est fire-and-forget. JetStream est la couche de persistance ajoutée en 2020 : flux durables, replay par séquence ou temps, curseurs consommateur, expiration de messages et fenêtres de déduplication par sujet. JetStream utilise Raft pour la réplication. Un cluster JetStream à 3 nœuds est le déploiement standard du cœur tactique — le quorum survit à la perte d'un nœud, et les flux se répliquent sans coordinateur Zookeeper séparé.

NATS gagne quand le trafic dominant est constitué de petits messages fréquents à faible latence entre services — commandes, mises à jour de pistes fusionnées, RPC microservice sur sujets request-reply. C'est le bus par défaut pour le trafic service-à-service à l'intérieur d'un moteur de fusion.

Où il échoue : la réplication JetStream est excellente à l'intérieur d'un cluster mais n'est pas conçue pour traverser un WAN dégradé. Les leaf nodes peuvent étendre une topologie NATS vers les périphériques, mais si le leaf est hors ligne pendant des heures, la fenêtre de rattrapage est bornée par la rétention du flux — pas par les attentes du leaf. Traiter NATS comme le bus du cœur, pas comme le bus longue distance.

Compromis de tolérance aux pannes : le quorum Raft de JetStream exige qu'une majorité de répliques accuse réception d'une écriture. Dans un cluster à 3 nœuds cela signifie deux accusés. Si un nœud est en maintenance et qu'un second perd son disque, les écritures stallent — le cluster préserve la cohérence au prix de la disponibilité. Pour un cœur tactique c'est le bon choix ; la cohérence de l'image opérationnelle n'est pas négociable. Mais le modèle opérateur compte : ne pas exécuter de clusters JetStream à trois nœuds où deux nœuds partagent un point unique de défaillance comme un seul switch ou une seule alimentation.

3. Apache Kafka

Kafka est le champion de la durabilité. Un journal append-only par partition, facteur de réplication configurable par topic, rétention mesurée en jours ou semaines, et un modèle consommateur qui laisse les nouveaux clients rejouer l'historique depuis l'offset zéro. Pour l'AAR, la journalisation d'audit et l'analyse sur données opérationnelles historiques, Kafka est presque toujours la bonne réponse.

C'est aussi cher. Un cluster Kafka de production veut trois brokers minimum, des disques locaux rapides, des gigaoctets de cache de page, et soit Zookeeper (hérité) soit KRaft (actuel, depuis Kafka 3.3 GA fin 2022, défaut en 3.5+). Le rééquilibrage de partitions sous partition réseau est un risque opérationnel connu. La coordination de groupes consommateurs suppose une connexion stable au broker coordinateur du groupe.

Le modèle « Kafka pour tout » qui fonctionne dans les shops cloud-native échoue à la périphérie tactique pour trois raisons. D'abord, l'empreinte de ressources est mauvaise — un broker JVM sur une boîte périphérique sans ventilateur perd contre un binaire NATS à chaque fois. Ensuite, la forte durabilité par défaut de Kafka vous punit sur une liaison à forte perte : les producteurs stallent en attendant les accusés. Enfin, la complexité opérationnelle (config broker, stratégie de partitionnement, réglage de rétention, monitoring ISR) est injustifiable quand la boîte est non surveillée en position avant.

Kafka appartient au niveau stratégique — le cluster arrière qui ingère les flux d'événements agrégés depuis les passerelles déployées en avant et les sert aux analyses, pipelines de données d'entraînement et archives long terme.

4. MQTT

MQTT a été conçu en 1999 pour la télémétrie de pipelines pétroliers sur liaisons satellite — exactement le profil réseau qu'un réseau de capteurs tactique présente aujourd'hui. Overhead d'en-tête minuscule (en-tête fixe de 2 octets dans le cas minimal), trois niveaux de qualité de service (0 fire-and-forget, 1 au-moins-une-fois, 2 exactement-une-fois), et une hiérarchie de topics qui se mappe naturellement aux structures capteur → unité → échelon.

MQTT 5.0, finalisé en 2019, a ajouté les fonctionnalités qui le rendent opérationnellement sérieux pour la défense. Abonnements partagés ($share/group/topic) load-balancent un topic à travers un groupe de consommateurs — utile pour le traitement en fan-out de données capteur. Intervalles d'expiration de messages écartent les données tactiques périmées automatiquement au broker. Propriétés utilisateur transportent les étiquettes de classification et marquages de diffusion comme métadonnées de message. Alias de topic compriment les longues chaînes de topics répétées en un seul octet après la première publication — un vrai gain sur une radio 9600 bps.

Le côté broker est mature : Mosquitto pour petites empreintes, EMQX ou HiveMQ pour déploiements clusterisés plus grands avec abonnements partagés et pontage. Les trois tournent sur du matériel de classe périphérique. MQTT-SN (Sensor Networks) étend le protocole sur transports non-TCP pour le vraiment minuscule — capteurs sur batterie sans pile IP.

La faiblesse de MQTT est la durabilité. Les sessions persistantes et QoS 2 donnent une livraison fiable à un client connu, mais MQTT n'est pas un journal d'événements — il n'y a pas de sémantique de replay par offset. Si un consommateur se déconnecte au-delà de l'expiration de sa session, les messages sont perdus. Pour la télémétrie capteur c'est acceptable. Pour une piste d'audit non.

5. RabbitMQ et AMQP

RabbitMQ précède la vague de messagerie cloud-native et gagne toujours sa place. Le modèle AMQP 0-9-1 — échanges, bindings, files — donne une flexibilité de routage que les bus pub-sub ne peuvent égaler. Échanges topic avec bindings wildcard, échanges header pour routage basé sur contenu, files dead-letter pour messages échoués, TTL par file, et accusé par message avec compteurs de redélivrance.

Pour les workflows où un message doit être traité exactement une fois par exactement un worker, avec sémantique d'accusé et de retry explicite, RabbitMQ est toujours la réponse la plus propre. Exemples dans une pile C2 : workflows de tâche où chaque tâche va à un opérateur, requêtes de géocodage qui touchent un service externe, jobs OCR contre imagerie capturée. Ce sont des problèmes de file, pas de flux, et la sémantique de file est ce que RabbitMQ fait.

L'observabilité est l'autre force discrète — l'UI de gestion, l'exportateur Prometheus et les métriques par file en font le plus facile des quatre à opérer à 03h00 quand quelque chose ne va pas. Pour une petite équipe ops gérant un nuage tactique non surveillé, cela compte.

Les limites de RabbitMQ apparaissent à très haut débit (ce n'est pas un bus à million de messages par seconde) et sur réseaux instables (le modèle AMQP orienté connexion déteste les chutes de liaison). À utiliser pour la couche workflow, pas pour la lance à incendie de télémétrie.

6. Ponter les bus

Les systèmes C2 de production exécutent deux ou trois bus simultanément. Un déploiement représentatif : MQTT à la périphérie pour le trafic capteur et radio, NATS dans le cœur tactique pour les commandes service-à-service et pistes fusionnées, Kafka au niveau stratégique pour l'archive d'événements durables. RabbitMQ peut apparaître aux côtés de NATS pour la couche workflow.

Les ponts sont des composants de première classe, pas des après-coups. Une passerelle MQTT-vers-NATS s'abonne à des topics MQTT sélectionnés, transforme la charge utile au schéma interne canonique, et republie sur un sujet NATS. Un pont NATS-vers-Kafka consomme les flux JetStream et produit vers les topics Kafka avec la même stratégie de clé de partition. Traduction de schéma, gestion de rétropression et republication idempotente sur redémarrage du pont sont les parties difficiles — pas la connexion elle-même.

Construire les ponts avec la même discipline d'ingénierie que tout autre service : health checks, métriques, une procédure de replay définie au redémarrage et un ownership clair. Le mode d'échec classique est un pont qui laisse silencieusement tomber des messages sous charge parce que sa file interne a débordé.

7. Sécurité et classification

Chaque bus parle TLS. Chaque bus supporte le TLS mutuel avec certificats client. C'est nécessaire, pas suffisant.

L'isolation par enclave est la couche suivante : une instance de broker séparée avec une autorité de certification séparée pour chaque niveau de classification. Le bus dans l'enclave SECRET ne parle jamais directement au bus dans l'enclave UNCLASSIFIED. La diffusion cross-domain passe par un guard approuvé ou une solution cross-domain qui retire, transforme et republie — jamais par un pont de broker.

Les ACL par topic sont la troisième couche. Sur NATS, comptes et permissions de sujet. Sur MQTT, fichiers ACL du broker ou plugin. Sur Kafka, ACL via l'API AdminClient. Sur RabbitMQ, permissions utilisateur-vhost-ressource. Le default-deny est la seule posture acceptable : un service peut publier et s'abonner exactement aux topics que son rôle requiert, et aucun autre.

Les métadonnées de message portent les étiquettes de classification — propriétés utilisateur MQTT 5, en-têtes NATS, en-têtes Kafka. Le broker n'applique pas la sémantique de classification ; les services consommateurs et le guard cross-domain le font. Le broker applique qui peut lire quel topic.

Idée clé : Le bus de messagerie fait partie de la frontière de sécurité, pas séparé d'elle. Traiter la configuration du broker — ACL, TLS, isolation de comptes — avec la même rigueur que la conception d'application offline-first et la conformité symbologique. Une ACL mal configurée est un déversement de classification en attente d'arriver.

8. Cadre de décision

Évaluer chaque classe de trafic sur quatre axes :

Budget de latence. RPC service-à-service sous-milliseconde : NATS. Dizaines de millisecondes pour télémétrie capteur : MQTT. Secondes pour ingestion d'archive : Kafka. Étapes de workflow par message avec accusés : RabbitMQ.

Débit. Jusqu'à ~10k messages/s par topic sur matériel modeste : n'importe lequel des quatre. 100k+ soutenu par topic : NATS ou Kafka. Millions à travers de nombreux topics : Kafka. Fan-in capteur depuis des milliers de clients à faible débit : MQTT avec abonnements partagés.

Durabilité. Pas de replay requis : NATS de base ou MQTT QoS 0/1. Replay dans une session ou courte fenêtre : NATS JetStream, sessions persistantes MQTT. Replay multi-jours niveau audit : Kafka. Accusé par message avec retry et dead-letter : RabbitMQ.

Réalité réseau périphérique. Radio 9600 bps avec 30 % de perte : MQTT, avec alias de topic et QoS 1. LAN tactique dans un véhicule : NATS. WAN stratégique vers cluster arrière : Kafka avec passerelle en façade. Satcom intermittent : MQTT pour télémétrie, producteur Kafka asynchrone avec spool local pour archive.

Construisez la matrice pour votre système spécifique. Chaque classe de trafic se mappe à un bus. Les ponts entre eux sont explicites. Le déploiement exécute les bus dont il a besoin et pas plus — ajouter un bus a un coût opérationnel, et ce coût est payé chaque quart, pas seulement à l'intégration.