# maclustr-dispatch — `mld` Orchestrateur de déploiement du cluster MacLustr. Il tourne **sur la passerelle M1M32** et déploie chaque application sur le **nœud le plus efficient** du cluster, via le LAN (192.168.2.x, clé SSH `maclustr-m1m32` autorisée sur tous les nœuds). ``` laptop ──ssh──▶ M1M32 (passerelle : spbgit + mld + staging ~/dispatch/stage) │ scan live (RAM libre, charge, disque, ports, runtimes) │ choix du nœud (score) → rsync LAN → PM2/launchd → ngrok → healthcheck → registre ▼ M3U96a · M3U96b · M2U64 · M4M64a · M4M64b · M4M36 · M2M32 · … ``` ## Depuis le laptop `~/Desktop/cluster-skill/mld ` relaie à la passerelle et rapatrie le registre (`cluster-skill/cluster-registry.json`). | Commande | Effet | |---|---| | `mld scan` / `mld nodes` | sonde (ou relit) ressources + runtimes des nœuds | | `mld apps` / `mld show ` | manifestes et emplacement courant | | `mld pick ` · `mld pick --ram 4 --runtime node` | classement des nœuds pour une app / un besoin | | `mld plan [apps…]` | plan de placement global (glouton, projection des ressources) | | `mld stage ` | pousse un dossier du laptop dans le staging de la passerelle | | `mld deploy [--node X]` | staging → nœud choisi (auto sans `--node`), démarrage, santé, registre | | `mld move [--to X] [--from Y]` | migration : pré-copie à chaud, arrêt, delta final, déploiement, santé, retrait (rollback auto) | | `mld stop/start/restart ` · `mld logs ` · `mld health ` | cycle de vie | | `mld status [--live]` · `mld registry` | registre (`~/dispatch/registry.json` sur M1M32) | | `mld subscribers [--add app\|node NOM CHEMIN] [--remove …]` · `mld registry --push` | abonnés du registre (copie poussée à chaque sauvegarde) · rediffusion manuelle | | `mld retire ` | arrête et efface la copie du nœud | | `mld prepare [--ka-helpers]` | installe node/pnpm/pm2/ngrok/python via Homebrew, config ngrok, pm2 startup, marqueur ; `--ka-helpers` = pousseur Ka + alias `gitsrv` + clé autorisée sur la passerelle | | `mld bootstrap ` | Xcode CLT + Homebrew sur un Mac vierge (sudo NOPASSWD temporaire) | | `mld import ` | brouillons de manifestes depuis les PM2 vivants d'un nœud (`~/dispatch/apps/_drafts/`) | ## Manifestes (`~/dispatch/apps/.json`, mode 600) Un manifeste décrit *comment* tourne une app : `dir` (même chemin absolu sur tous les nœuds), `processes` (PM2 : script, args, interpreter, cwd, env, cron_restart…), `ngrok` (domaine réservé → processus `-ngrok`), `launchd` (plists générés), `requires` (runtimes, RAM, ports), `placement` (`pin`, `prefer`, `avoid` + raison), `sync_excludes`, `extra_paths`, `env_overrides` (réécriture de `.env` au déploiement), `hooks.post_sync` / `post_start`, `ka_repo` (inscription au pousseur). Gabarits : `{{HOME}}`, `{{NODE}}`, `{{LAN_IP}}`, `{{IP:}}`. ## Scoring `0.45·RAM libre après placement + 0.25·cœurs idle + 0.10·disque + 0.15·cœurs − 0.04·apps hébergées (+0.15 prefer, +0.05 si déjà sur ce nœud)`. Inéligible : hors ligne, rôle gateway (sauf épingle), runtime manquant, RAM libre < besoin + 2 Go, disque, port occupé (hors nœud d'origine). ## Registre = source de vérité des emplacements (consommateurs) `~/dispatch/registry.json` (M1M32) est la **seule** référence « quelle app tourne sur quel nœud ». Personne ne code plus d'emplacement en dur : les consommateurs lisent le registre. - **Push** : à chaque sauvegarde (deploy/move/stop/start/retire), `registry.save()` pousse une copie atomique chez chaque abonné de `~/dispatch/subscribers.json` (`mld subscribers` pour lister/ajouter). Un abonné `{"app": X, "path": P}` est résolu dans le registre lui-même (nœud + dir de X) — il suit les migrations. Abonnés au 2026-09-04 : `admin-ka` → `data/registry.json` (console administration-ka.com : sites, projets Claude Code, sweep des nœuds, CLAUDE.md de l'orchestrateur multi-sites) et `ka2` → `~/ka-guardian-spool/registry.json` (gardiens ka2/ka4/ka6 : nœud, dir, port, process PM2 de chaque service surveillé). - **Pull** : tout nœud peut aussi tirer `ssh gitsrv cat ~/dispatch/registry.json` (alias `gitsrv` = M1M32, clé `ka_guardian_ed25519` autorisée). admin-ka le fait toutes les 2 min, le service launchd `com.ka.registry-sync` de M4M36 toutes les 2 min (chaîne zsh pure : macOS 26 LNP interdit le LAN aux descendants de python). - Champs consommés : `node`, `ip`, `port`, `domain`, `dir`, `processes` (PM2), `launchd`, `status`, `updated`. Garder ces noms stables. ## Ajouter un nœud 1. Activer *Session à distance* sur le Mac, autoriser la clé de la passerelle (`~/.ssh/id_ed25519.pub` de M1M32) et celle du laptop dans `~/.ssh/authorized_keys`. 2. `echo > ~/.maclustr-node` sur le nœud ; ajouter l'alias dans `mld/config.py` (`NODES`). 3. `mld discover` → `mld bootstrap ` (si Mac vierge) → `mld prepare `. ## Fichiers - `mld/config.py` inventaire statique, rôles, chemins, détection des runtimes - `mld/nodes.py` découverte LAN, sonde · `mld/picker.py` score/plan · `mld/deploy.py` fetch/deploy/move/health - `mld/render.py` ecosystem PM2 + plists · `mld/prepare.py` runtimes/bootstrap/ka-helpers · `mld/importer.py` - `assets/` pousseur Ka (script + plist) Pas de dépendance : Python 3 système (3.9) + ssh/rsync (GNU rsync via Homebrew sur la passerelle).