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
- Vrai Claude Code, jamais l'API Messages. Le backend lance le binaire
claude(headless :claude -p --output-format stream-json --verbose --include-partial-messages), aveccwd= 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). - Options Claude Code dans l'UI : modèle par session (Fable 5 défaut, Opus 5, Sonnet 5, Haiku,
--model), mode de permissions (defaultavec 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 » =
autoAllowAllsur 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 ».
- « Tout autoriser » =
- 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). - É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. - 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é). - 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 <nœud> 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 WSserver/registry.js— topologie vivante (2026-09-04) : où tourne chaque app Ka = registre mld de la passerelle (M1M32:~/dispatch/registry.json). Copiedata/registry.jsonpoussée parmld(abonnéadmin-ka) et surveillée (fs.watchFile), + tirage ssh toutes les 2 min (M1M32puisgitsrv). FournitSITES/getSites()(monitoring),getProjects()/findProject()(projets Claude Code, idsapp@nœudrésolus par app),selfNode()(nœud de la console = entréeadmin-kadu registre),sshTarget()(alias ssh sinonuser@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.mdcontextuel).KA_CATALOGne 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, alerteeco_alert kind=registry+ message WSprojectsquand 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/configen 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.
ControlPathssh avec hostnames .local trop long →%C.- Sans
--model, les nœuds retombent sur leur défaut local (Sonnet) → toujours passer--modelexplicitement. - Auth headless par
ANTHROPIC_API_KEYde~/.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
# 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 '<nouveau>' # changer le mot de passeRegistre 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énementpv(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 + cacheipinfoip-api avechosting/proxy/ASN, reclassement rétroactif quand l'IP se résout, anti-floodmaxIpPerHour). 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 ancienneshits/dailysont 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 dansconfig.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/).