# CLAUDE.md — administration-ka.com (Admin-Ka v2) Console d'administration du Groupe KA : app chat mobile-first qui pilote le **vrai Claude Code CLI** sur le cluster MacLustr, plus un monitoring temps réel de tous les sites du groupe. ## Exigences non négociables 1. **Vrai Claude Code, jamais l'API Messages.** Le backend lance le binaire `claude` (headless : `claude -p --output-format stream-json --verbose --include-partial-messages`), avec `cwd` = le repo de l'app choisie. Chaque app KA s'exécute **sur son nœud de déploiement** (via SSH pour les nœuds distants) — le repo de prod est la seule source de vérité, aucun clone. Tous les événements du stream (texte, thinking, tool_use, tool_result, result) sont relayés et rendus comme dans le terminal (diffs colorés, blocs outils repliables, coût/tokens). 2. **Options Claude Code dans l'UI** : modèle par session (**Fable 5 défaut**, Opus 5, Sonnet 5, Haiku, `--model`), mode de permissions (`default` avec cartes **Autoriser / Toujours (cet outil) / Tout autoriser / Refuser**, `acceptEdits`, `plan`, bypass derrière confirmation), choix du projet/cwd, `/clear` `/compact` `/cost` `/resume`, affichage live du modèle actif, session ID, coût cumulé, tokens de contexte (+ %), file d'attente de message, chrono d'exécution, copie des blocs de code. - **« Tout autoriser »** = `autoAllowAll` sur la conversation : le serveur approuve TOUTES les permissions suivantes jusqu'à la fin — la tâche va au bout sans jamais réattendre l'utilisateur (testé : write auto-approuvé, 0 carte). C'est l'exigence « si on coche tout autoriser, ça finit vraiment la tâche ». 3. **Sessions détachées + reprise durcie** : le process claude ne dépend JAMAIS du WebSocket. Transcript JSONL persisté ; à la reconnexion l'app rejoue l'historique manqué puis reprend le live. **Reprise auto** : l'app rouvre la dernière conversation au lancement (`lastChatId`) et **force la reconnexion + resync au retour au premier plan** (`visibilitychange`/`online`, iOS coupe le WS à l'écran verrouillé). États : en cours / en attente de permission / inactive. Reconnexion auto backoff + indicateur. Notifications in-app (fin de tâche, permission attendue). 4. **Écosystème** : health checks HTTPS toutes les 45 s des 13 sites KA depuis M3U96a (latence, code, SSL), sweep des nœuds toutes les 5 min (charge/RAM/disque + PM2 cpu/mem/status + dernier commit), historique SQLite (`data/eco.sqlite`, rétention 35 j), incidents (2 échecs consécutifs → alerte, rétablissement → alerte verte), uptime 24h/7j/30j, moy/p95, sparklines + graphique 24 h + barres d'uptime 14 j, push temps réel via le même WebSocket. **Santé des 5 nœuds** (barres charge/RAM/disque + apps up), **vue Incidents globale**, et **actions d'admin par site** : redémarrer PM2 (`/api/admin/action`), logs récents (`/api/admin/logs`), git & commits (`/api/admin/commits`) — exécutées localement ou via ssh sur le nœud du site. 5. **Identité Groupe KA** : crème `#F2F1EC`, orange `#F97316` (traits de graphe `#e05f00`), encre `#1a1611`, cartes à bords noirs 2 px + ombres décalées, pills, typo ronde, pastille « Ka ». Header fixe + bottom nav (Chat / Sessions / Écosystème / Réglages) avec safe-areas. Statuts JAMAIS en couleur seule (● ▲ ■ + libellé). 6. **Rien de simulé** : tout est branché sur le vrai CLI et les vrais sites. ## Architecture ``` iPhone ⇄ wss://www.administration-ka.com (ngrok) ⇄ backend :3300 (M3U96a) backend ⇄ claude CLI (local, apps M3U96a) backend ⇄ ssh claude CLI (apps distantes, repo de prod) └ tunnel SSH inverse -R : cartes de permission (perm-mcp MCP) backend ⇄ monitor.js : health checks + sweep nœuds + SQLite + push WS ``` - `server/registry.js` — **topologie vivante** (2026-09-04) : où tourne chaque app Ka = registre mld de la passerelle (`M1M32:~/dispatch/registry.json`). Copie `data/registry.json` poussée par `mld` (abonné `admin-ka`) et surveillée (fs.watchFile), + tirage ssh toutes les 2 min (`M1M32` puis `gitsrv`). Fournit `SITES`/`getSites()` (monitoring), `getProjects()`/`findProject()` (projets Claude Code, ids `app@nœud` résolus par app), `selfNode()` (nœud de la console = entrée `admin-ka` du registre), `sshTarget()` (alias ssh sinon `user@IP LAN`), `topologyText()` (bloc injecté dans les prompts système), régénère `~/ka-orchestrator/CLAUDE.md` à chaque changement et outille les nœuds hébergeurs (`~/.adminka/perm-mcp.js`, `~/.claude/CLAUDE.md` contextuel). `KA_CATALOG` ne contient que la connaissance Ka (libellés, launchd des gardiens, pm2 non déductibles) — **plus jamais d'emplacement en dur**. API : `GET /api/registry`, `POST /api/registry/refresh`, `POST /api/registry/tooling` ; UI : carte « Registre » dans Réglages, alerte `eco_alert kind=registry` + message WS `projects` quand une app bouge ; une conversation dont l'app a déménagé est réalignée à son prochain tour (`realignChat`, nouvelle session Claude). - `server/server.js` — HTTP + WS + orchestration claude + états de session. - `server/monitor.js` — collecteur Écosystème (node:sqlite intégré, aucune dépendance native) ; sites et nœuds = `registry.js`. - `server/perm-mcp.js` — serveur MCP stdio pour `--permission-prompt-tool mcp__adminka__approve` ; copié automatiquement dans `~/.adminka/` de chaque nœud hébergeur connu du registre (`ensureNodeTooling`, contrôle toutes les 6 h ou bouton « Vérifier l'outillage » dans Réglages). - `public/` — frontend une page, 4 onglets, aucun framework. ## Pièges connus (ne pas re-découvrir) - **Tailscale inter-nœuds bloqué (ACL)** : depuis M3U96a, joindre les nœuds via les noms Bonjour `.local` (config `~/.ssh/config` en place). M2U64 = sous-réseau 192.168.0.x. - **IPv4 LAN non routée entre nœuds** (EHOSTUNREACH) : les permissions distantes passent par le tunnel SSH inverse, pas par le LAN. - `ControlPath` ssh avec hostnames .local trop long → `%C`. - Sans `--model`, les nœuds retombent sur leur défaut local (Sonnet) → toujours passer `--model` explicitement. - Auth headless par `ANTHROPIC_API_KEY` de `~/.claude/.env` (pas de login OAuth sur les nœuds). - Chaque run reçoit `--append-system-prompt` : après toute modif → rebuild + pm2 restart + healthcheck + commit/push **spbgit** (origin, pas GitHub). ## Exploitation ```bash # sur M3U96a launchctl kickstart -k gui/501/io.adminka.backend # redémarrer backend launchctl kickstart -k gui/501/io.adminka.ngrok # redémarrer tunnel tail -f ~/apps/admin-ka/data/backend.err.log node server/set-password.js '' # changer le mot de passe ``` Registre des déploiements : **`M1M32:~/dispatch/registry.json`** (orchestrateur `mld`, source de vérité — `cluster-deployments.json` du laptop est l'ancien registre, ne plus s'en servir). Repo : spbgit `gitsrv:srv/git/admin-ka.git` (bare sur M1M32). ## v3 (2026-08-26) — Control Center : refonte UI + analytics humains **UI** : desktop-first — sidebar gauche (≥1020 px) + topbar (titre, incidents, thème, statut) ; bottom nav mobile réduite à 5 (Vue d'ensemble, Claude Code, Écosystème, Visiteurs, Plus). Nouvelle page **Vue d'ensemble** (KPIs humains+éco+Claude, chart 30 j, chips écosystème, incidents, anomalies). **Dark mode** réel (`ka_theme` auto/clair/sombre, variables `--edge/--shadow/--code-bg/...`). Sessions et Prompts en listes denses filtrables (recherche/app/statut ; favoris ★, duplication, compteur d'usage sur les prompts). **Analytics v2 (`server/analytics.js`) — définitions officielles** : - **Visiteur humain** : vid classé `human` = UA navigateur + JS exécuté + réseau non-datacenter/proxy + pas interne + pas de flood. - **Visiteur unique** : vid humain distinct dans la fenêtre. **Session** : trou > 30 min = nouvelle session (`sessionMin`). **Page vue** : événement `pv` (navigation réelle, SPA incluse) — un heartbeat n'est JAMAIS une page vue. **En ligne** : dernier événement humain < 120 s (`activeSec`) — expire tout seul. - **Classes** : `human / crawler / datacenter / automation / internal` — classées à l'insertion (UA regex + cache `ipinfo` ip-api avec `hosting/proxy/ASN`, reclassement rétroactif quand l'IP se résout, anti-flood `maxIpPerHour`). Les métriques principales n'affichent QUE les humains ; le reste est visible dans la barre de répartition et le toggle « Tout le trafic ». - Tables : `events` (brut, rétention 45 j), `ipinfo` (cache IP-intelligence), `daily2` (rollup quotidien par classe, long terme), `meta.migrated_at` (frontière legacy). Les anciennes `hits/daily` sont CONSERVÉES et affichées en pointillé « ancien comptage v1 (non filtré) » — ne jamais comparer directement les deux séries. - **Beacon v2** : `public/ka-a.js` (vid/sid localStorage, pv SPA via pushState, heartbeat 25 s seulement onglet visible + interaction < 60 s). Le beacon v1 (ping 60 s) reste supporté : le serveur dé-duplique (même page ~60 s = heartbeat) et sessionne en mémoire. - API : `/api/analytics/{summary,series/:site|_all,site/:site,realtime,anomalies,inspect?ip=|vid=,classes,config(GET/PUT)}`. Config runtime dans `config.json → analytics` (fenêtres, IP/UA internes, bots custom, ASNs exclus) — éditable dans Réglages. - Tests : `node server/test-analytics.mjs` (28 scénarios : humain/refresh/SPA, Googlebot, uptime bot, IP AWS, curl, interne, dédup v1, invariants uniques≤sessions≤pv, expiration en ligne) — à lancer depuis une COPIE (écrit dans data/).