IPC et bus

Le système nerveux qui connecte les pods entre eux : Redis Streams pour le temps réel, bus JSONL pour l'audit trail, consumer groups pour la coordination.

Quatre couches de communication

Le sous-système IPC de Nika OS combine quatre couches, chacune avec un rôle distinct. Toutes sont fail-open : si Redis est down, les pods continuent de tourner en mode dégradé plutôt que d’échouer en cascade.

CouchePattern RedisTTLUsage
Signalingnika:ipc:{job_id} (STREAM)2 hEvents : result, signal, request, error
Working Memorynika:wm:{job_id}:{artifact} (HASH)1 hRésultats intermédiaires < 512 kB
Contractsnika:contract:{job_id} (HASH)2 hPipeline multi-pod : steps, roles, I/O
Entity Feednika:feed:entities (STREAM)24 hTous les changements PROJ/JOB/TASK

Consumer groups

Le stream nika:feed:entities est consommé par trois groups, qui se partagent les événements selon leur rôle :

  • cg:orchestrator — le pod Alpha reçoit tous les événements. Il s’en sert pour observer l’essaim sans avoir à interroger chaque pod individuellement.
  • cg:worker — les pods workers sont load-balanced dans ce group. Quand un nouveau job apparaît, un seul worker est notifié, ce qui évite les races.
  • cg:monitor — le dashboard (Grafana, K3s pods de monitoring) reçoit aussi tous les événements pour les métriques temps réel.

Cette séparation est ce qui permet à Alpha de garder sa fenêtre de contexte propre. Alpha n’a pas à lire les logs de chaque pod : il s’abonne au feed entités et reçoit seulement les transitions significatives (created, completed, failed). Si Alpha veut un détail, il lit le bus JSONL ou interroge Qdrant.

Dual-write : Redis + Qdrant

Les événements significatifs (completed, failed, created pour les hiérarchies racines) sont dual-written :

  • en Redis Streams pour la latence temps réel ;
  • en Qdrant pour la persistance long terme et le rappel sémantique.

L’événement en Qdrant porte l’enveloppe NIKA_META complète. Six mois plus tard, on peut interroger « combien de jobs domain=hooks ont échoué avec intent=refactor en avril 2026 ? » et obtenir une réponse instantanée.

Bus JSONL : l’audit trail

En parallèle de Redis, chaque message d’orchestration est append-only dans un fichier JSONL :

  • _bus/alpha_bus.jsonl — canal global d’Alpha.
  • _bus/channels/{entity_id}.jsonl — un fichier par entité PROJ/JOB/TASK.

Pourquoi un fichier plat alors que Redis fait le travail ? Trois raisons :

  1. Survie aux pannes Redis — les pods peuvent encore loguer.
  2. Audit hors-ligne — un opérateur peut grep six mois d’historique sans sortir le terminal.
  3. Forensics — quand quelque chose va mal, le bus JSONL est la dernière source de vérité, indépendante des autres systèmes.

Les types de messages standards :

session_start         — démarrage d'un pod ou d'Alpha
session_end_summary   — fin avec résumé enrichi
subagent_completed    — un pod a fini, summary ingéré en Qdrant
review_request        — le pod parent doit valider un livrable
autonomous_dispatch   — dispatch déclenché par autonomy_engine (3% probabiliste)
job_completed         — entity hiérarchique terminée

Télémétrie Redis Streams

Chaque appel d’outil dans un pod déclenche un XADD sur le stream agent:events:{session_id} (maxlen=5000, TTL 24 h). En parallèle, le hook PostToolUse met à jour :

  • agent:state:{session_id} (HSET heartbeat, TTL 2 h) — le pod est-il encore vivant ?
  • agent:metrics:{date} (HINCRBY, TTL 7 j) — compteurs journaliers par outil.
  • nika:ipc:metrics:{date} (HINCRBY, TTL 7 j) — compteurs spécifiques IPC.

Le pod observe sans bloquer. Si la connexion Redis flanche, l’appel d’outil réussit quand même — la télémétrie est best-effort, pas critique pour l’exécution.

Le pattern fail-open

Tout le sous-système IPC suit un pattern strict : fail-open avec TTL partout. Un pod qui ne peut pas joindre Redis :

  • continue son exécution locale ;
  • log l’erreur en local (fichier dans logs/) ;
  • n’écrit pas de message bus JSONL pendant la durée du downtime ;
  • reprend les acks normaux quand Redis revient.

Aucun pod ne doit jamais se bloquer parce qu’un système annexe est lent ou indisponible. C’est une décision de design conservatrice, justifiée par le fait que les pods tournent souvent en boucle longue (heures), et que tout hang silencieux finit par coûter cher en débogage.

En une phrase

L’IPC de Nika OS est conçu pour que des dizaines de pods coexistent sans se marcher dessus, sans saturer la mémoire d’Alpha, et sans tomber en cascade si un composant flanche. Redis porte le temps réel ; Qdrant porte la mémoire ; JSONL porte la preuve.