#!/bin/zsh # ============================================ # KA Guardian — synchronisation du registre mld + sonde des runners (100 % zsh) # Le registre des déploiements (M1M32:~/dispatch/registry.json) dit où tourne # chaque app du Groupe Ka. mld le POUSSE déjà dans le spool à chaque sauvegarde ; # ce service le TIRE en plus toutes les 2 min (ceinture et bretelles), puis # sonde le runner :7791 de chaque nœud hébergeur pour que les orchestrateurs # sachent, AVANT de dépêcher une mission, si le nœud est équipé. # Chaîne launchd → zsh → /usr/bin/ssh : macOS 26 « Local Network Privacy » # refuse le LAN aux descendants de python (homebrew) — jamais de python ici. # ~/ka-guardian-spool/registry.json ← copie du registre (écriture atomique) # ~/ka-guardian-spool/runners.json ← {"": {"ok":bool,"node":…,"busy":…,"ts":epoch}} # ============================================ export PATH=/usr/bin:/bin:/usr/sbin:/sbin SPOOL=$HOME/ka-guardian-spool KEY=$HOME/.ssh/ka_guardian_ed25519 LOG=$SPOOL/registry-sync.log INTERVAL=${KA_REGISTRY_INTERVAL:-120} mkdir -p $SPOOL log() { print "$(date '+%F %T') $*" >> $LOG; } # JSON minimal sans python : extraire les IP uniques des apps du registre. registry_ips() { # les entrées ont la forme "ip": "192.168.2.83" grep -o '"ip": *"[0-9.]*"' $SPOOL/registry.json 2>/dev/null | grep -o '[0-9.]*[0-9]' | sort -u } while true; do # 1. tirer le registre (alias gitsrv = passerelle M1M32, clé ka_guardian autorisée) if /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=8 gitsrv 'cat ~/dispatch/registry.json' > $SPOOL/registry.json.pull 2>>$LOG \ && grep -q '"apps"' $SPOOL/registry.json.pull; then if ! cmp -s $SPOOL/registry.json.pull $SPOOL/registry.json; then mv -f $SPOOL/registry.json.pull $SPOOL/registry.json log "registre mis à jour ($(grep -o '"updated": *"[^"]*"' $SPOOL/registry.json | head -1))" else rm -f $SPOOL/registry.json.pull fi else rm -f $SPOOL/registry.json.pull log "tirage du registre impossible (gitsrv injoignable) — copie précédente conservée" fi # 2. sonder les runners des nœuds hébergeurs (le journal des sondes ne garde que le dernier cycle) : > $SPOOL/registry-sync.probe.log out="{" first=1 for ip in $(registry_ips); do resp=$(/usr/bin/ssh -i $KEY -o BatchMode=yes -o ConnectTimeout=6 -o StrictHostKeyChecking=accept-new \ -o ControlMaster=auto -o ControlPath=/tmp/kg-cm-%h -o ControlPersist=120 \ simon-pierreboucher@$ip "/usr/bin/curl -s -m 4 http://127.0.0.1:7791/health" 2>>$SPOOL/registry-sync.probe.log) ts=$(date +%s) if [[ $resp == *'"ok":true'* ]]; then node=$(print -r -- $resp | grep -o '"node":"[^"]*"' | cut -d'"' -f4) busy=$([[ $resp == *'"busy":true'* ]] && echo true || echo false) row="\"$ip\":{\"ok\":true,\"node\":\"$node\",\"busy\":$busy,\"ts\":$ts}" else row="\"$ip\":{\"ok\":false,\"node\":null,\"busy\":false,\"ts\":$ts}" fi [[ $first -eq 1 ]] && first=0 || out+="," out+=$row done out+="}" print -r -- $out > $SPOOL/runners.json.tmp && mv -f $SPOOL/runners.json.tmp $SPOOL/runners.json sleep $INTERVAL done