1e1be7f627
Ignore les dépôts de référence imbriqués (linux-update-dashboard, nas-ops). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
401 lines
34 KiB
Markdown
401 lines
34 KiB
Markdown
# Étude d’architecture pour une webapp de mise à jour distante Linux
|
||
|
||
## Ce que montrent les deux dépôts
|
||
|
||
Le dépôt **linux-update-dashboard** est déjà très proche de votre besoin sur la partie orchestration web : il s’agit d’une application TypeScript presque entièrement composée d’un **frontend React 19 + Vite 8 + Tailwind CSS 4** et d’un **backend Hono**, avec **Drizzle ORM**, **SQLite**, **SSH2**, **WebSocket**, **highlight.js**, **Mustache**, **OIDC**, **WebAuthn**, **MQTT** et des scripts de planification. Le README et l’arborescence indiquent aussi des dossiers `client/`, `server/`, `ssh/`, `services/`, `routes/` et `db/`, ce qui montre une séparation assez propre entre UI, API, logique métier, stockage et exécution SSH. citeturn24view1turn39view0turn6view0turn6view1turn25search4turn25search5
|
||
|
||
Sur le plan d’exécution, **linux-update-dashboard** expose des routes API pour les systèmes, les mises à jour, les scripts, les réglages et les credentials, ainsi qu’une route **WebSocket** `/api/ws/systems/:id/output` dédiée au flux live des commandes. Son service `output-stream` publie des messages structurés (`started`, `output`, `phase`, `done`, `error`, `warning`) et conserve un buffer rejouable pour les clients qui se reconnectent. La couche SSH encapsule les commandes via `sh -c`, force `LC_ALL=C` et un `PATH` minimal pour stabiliser les sorties, et sait lancer des scripts détachés `nohup` avec fichiers de log et d’exit code, ce qui permet aux opérations longues de survivre à une coupure SSH. citeturn7view0turn12view1turn12view3turn11view0
|
||
|
||
Le dépôt **nas-ops** apporte l’autre moitié de la solution : des **scripts Bash simples, ciblés et JSON-friendly**. `nas-system-update` exécute `apt-get update -qq`, simule un `full-upgrade`, extrait les paquets upgradables et retourne un JSON avec `count`, `packages` et `reboot_required` en mode non interactif. `nas-system-upgrade` applique `apt-get full-upgrade -y` avec options `dpkg` défensives, puis renvoie un JSON de résultat. Côté Docker, `nas-docker-pull` inspecte les conteneurs, compare les IDs d’image avant/après `docker pull`, lit des labels comme le dépôt source et les versions, puis émet un JSON listant uniquement les conteneurs réellement concernés ; `nas-docker-up` remonte ensuite les stacks via `docker compose up -d --remove-orphans` avec gestion des fichiers d’environnement OMV et renvoie à son tour un JSON d’exécution. citeturn2view4turn3view3turn3view4turn4view0turn4view1turn4view2turn18view0turn18view1turn18view2
|
||
|
||
Le point important est donc le suivant : **linux-update-dashboard** apporte un excellent modèle d’**application web agentless par SSH**, tandis que **nas-ops** apporte un excellent modèle de **scripts shell déterministes qui produisent des JSON compacts**. Pour votre cas Debian, Ubuntu, Proxmox et Raspberry Pi, je recommande clairement de **fusionner les deux approches**, plutôt que de n’en reprendre qu’une seule. citeturn24view1turn3view3turn3view4turn4view1turn4view2
|
||
|
||
Il faut aussi noter une contrainte juridique : **linux-update-dashboard** affiche explicitement une licence **AGPL-3.0** sur GitHub. En revanche, sur la page consultée de **nas-ops**, je n’ai pas trouvé de mention explicite de licence dans la navigation GitHub. Concrètement, cela plaide pour mettre les deux dépôts en **références de travail dans le dossier de l’app**, mais en les considérant d’abord comme **sources d’inspiration et de vérification**, pas comme du code à recopier sans revue de licence. citeturn24view1turn24view0turn24view2
|
||
|
||
## Principes d’architecture à retenir
|
||
|
||
Le meilleur pattern pour votre projet est, à mon avis, un **backend Node.js/TypeScript qui orchestre**, mais **ne connaît pas la logique métier fine des mises à jour**. Toute la logique “comment mettre à jour APT”, “comment détecter les paquets”, “comment lister les stacks Docker”, “comment appliquer un `docker compose up -d` dans un dossier précis” doit vivre dans des **templates shell versionnés**, dérivés de l’esprit `nas-ops`, tandis que le backend gère l’inventaire, les droits, les jobs, les logs live, le chiffrement des secrets, l’historique et l’API. C’est exactement la séparation de responsabilités que la combinaison des deux dépôts rend possible. citeturn24view1turn3view3turn3view4turn4view1turn4view2
|
||
|
||
Je vous recommande donc une architecture en quatre couches. La première est la **couche UI**, sans machine prédéfinie au démarrage, où l’utilisateur ajoute des machines via un bouton `+`. La deuxième est la **couche API**, qui stocke les machines, les credentials, les templates, les jobs et les rapports. La troisième est la **couche worker**, qui lance les refreshs et les opérations longues en arrière-plan. La quatrième est la **couche SSH/script runtime**, qui pousse des commandes vers les hôtes et normalise leurs retours en JSON. Le dépôt `linux-update-dashboard` montre que ce schéma fonctionne bien pour du pilotage SSH multi-machines ; `nas-ops` montre qu’un script shell bien écrit peut déjà servir d’API machine. citeturn24view1turn7view0turn12view1turn3view3turn4view0turn4view1turn4view2
|
||
|
||
Pour votre cas précis, je garderais la règle suivante : **`update/check` en tâche de fond**, **`upgrade/full-upgrade/dist-upgrade/docker apply/reboot` en déclenchement manuel**. Cette séparation est cohérente avec la documentation APT, qui distingue nettement la resynchronisation des index (`update`) des opérations qui modifient réellement l’état de la machine (`upgrade`, `dist-upgrade`, `autoremove`, `clean`). Elle colle aussi à l’approche de `linux-update-dashboard`, qui distingue déjà les checks, les upgrades, l’autoremove et le reboot, avec certaines opérations exécutées en mode SSH-safe détaché. citeturn23view0turn24view1turn11view0turn12view3
|
||
|
||
Je déconseille en revanche de stocker vos commandes critiques uniquement dans la base. Il vaut mieux avoir un **registre de templates versionnés sur disque**, éditables depuis le frontend mais sauvegardés comme des ressources de projet, avec éventuellement des **overrides par machine**, exactement dans l’esprit des “script customizations” et des “per-system script overrides” du dépôt de référence. Cela facilitera énormément le travail avec Claude Code, les revues Git et l’évolution future vers des scripts de post-install, de réseau ou d’installation de paquets. citeturn24view1turn8view2
|
||
|
||
## Stack technique recommandée
|
||
|
||
Pour le **frontend**, je vous recommande de rester dans le même univers que `linux-update-dashboard` : **React + TypeScript + Vite**. React reste une base solide pour un dashboard à composants, Vite apporte une boucle de dev rapide, et le dépôt étudié montre déjà qu’un tel couple tient bien la charge sur ce type d’outil. Pour le design system, je recommande **shadcn/ui** plutôt qu’une grosse librairie opaque : la documentation officielle le présente comme une plateforme de distribution de composants accessibles et ouverts, avec un composant **Resizable** basé sur `react-resizable-panels`, ce qui est très utile pour votre volet terminal à droite. Pour les icônes, **Lucide React** est un très bon choix, avec composants SVG tree-shakables et typed. citeturn39view0turn19search3turn20search13turn20search1turn26search1turn26search2
|
||
|
||
Pour l’éditeur de templates, je prendrais **Monaco Editor**. Pour le terminal live, en revanche, je prendrais **xterm.js**. La raison est simple : Monaco est excellent pour éditer des scripts et des snippets avec coloration, tandis que xterm.js est un **vrai émulateur de terminal web**, avec support des séquences ANSI et un addon d’attache WebSocket. Autrement dit, Monaco pour **éditer les templates**, xterm.js pour **voir l’exécution en direct**. Votre intuition du “web terminal à droite avec coloration syntaxique” est donc réalisable, mais la bonne implémentation est plutôt “**coloration terminal ANSI + thème terminal**” que “éditeur de code live”. citeturn19search6turn19search1turn19search12turn7view0turn12view1
|
||
|
||
Pour le **backend HTTP/API**, **Hono** est une proposition cohérente. Le projet de référence l’utilise déjà, et sa documentation officielle le présente comme un framework small/simple/ultrafast, multi-runtime. Si vous voulez rester proche des patterns déjà observés, Hono est un bon choix. Si vous cherchiez un framework plus “entreprise”, Fastify ou NestJS seraient défendables, mais au vu des deux dépôts étudiés, Hono est le choix le plus naturel pour un premier jet propre et rapide. citeturn25search0turn25search4turn7view0
|
||
|
||
Pour le **stockage**, je vous recommande plutôt **PostgreSQL** que SQLite pour votre projet final, même si le repo étudié utilise SQLite. SQLite est parfait pour une app solo simple ; votre besoin, lui, évoque déjà plusieurs machines, rapports archivés, templates, logs, états de jobs, déduplication, intégration agent et évolution fonctionnelle. **PostgreSQL + Drizzle ORM** me paraît donc un meilleur point d’équilibre. Si vous voulez **minimiser l’infrastructure**, utilisez **pg-boss** pour la file de jobs sur PostgreSQL ; si vous avez déjà Redis et que vous voulez davantage de fonctions de queue natives comme dedup/throttle/flows, **BullMQ** est une alternative robuste. citeturn25search5turn25search13turn27search2turn27search5turn27search0turn27search12
|
||
|
||
Pour l’**exécution distante**, je recommande **agentless SSH** en v1, avec un compte dédié de maintenance, chiffrement des credentials côté serveur, vérification de host key, bastion/ProxyJump optionnel, et règles sudo minimales. `linux-update-dashboard` montre déjà des credentials chiffrés au repos, du host-key approval explicite et du ProxyJump ; son README précise aussi que l’application n’est pas pensée pour être exposée directement sur Internet, mais pour un réseau de confiance protégé par reverse proxy/TLS/VPN. Je reprendrais cette discipline de sécurité presque telle quelle. citeturn24view1turn13search0turn12view3
|
||
|
||
## Flux fonctionnels et contrats JSON
|
||
|
||
Votre flux de base peut être très simple côté produit. La page d’accueil démarre vide. Un bouton `+` ouvre un formulaire d’ajout machine contenant : **nom**, **OS**, **IP/hostname**, **port SSH**, **username**, **mode d’authentification** (mot de passe, clé SSH plus tard), **sudo password** si nécessaire, **activation de l’update automatique**, **templates activables** (`update`, `upgrade`, `full-upgrade`, `dist-upgrade`, `clean`, `autoremove`, `reboot`, `docker scan`, `docker pull`, `docker up`, `docker prune`), **proxy APT / apt-cacher-ng**, et **un ou plusieurs répertoires Docker Compose à surveiller**. Le backend effectue un `test-connection`, détecte les capacités, puis crée la tuile machine avec le cache d’état initial. Les routes et le workflow d’ajout de système déjà visibles dans `linux-update-dashboard` rendent cette approche très crédible. citeturn8view2turn24view1
|
||
|
||
Pour APT, il faut distinguer les sens exacts des commandes. Le manpage officiel d’APT rappelle que `update` resynchronise les index, que `upgrade` n’enlève pas de paquets installés et n’en installe pas de nouveaux, que `dist-upgrade` gère intelligemment les changements de dépendances, que `clean` vide le cache local des paquets récupérés, et que `autoremove` supprime les dépendances devenues inutiles. Pour **Proxmox**, les docs officielles insistent fortement sur la qualité des dépôts configurés et montrent des upgrades CLI autour de `apt update` puis `apt dist-upgrade`. Pour **Raspberry Pi OS**, la documentation officielle confirme qu’APT est bien le gestionnaire natif. Votre moteur de templates doit donc faire de l’**OS profile-aware**, pas du simple collage de commandes. citeturn23view0turn31search1turn31search11turn31search0turn31search2
|
||
|
||
Pour `apt-cacher-ng`, la doc Debian le décrit comme un **proxy de cache** pour les téléchargements de paquets, et la doc APT précise que les proxys APT se configurent via `Acquire::http::Proxy` et les options apparentées. Je vous conseille donc un réglage frontend par machine ou par template avec trois modes : **direct**, **proxy temporaire à l’exécution**, ou **proxy persistant dans `/etc/apt/apt.conf.d/`**. Cela vous donne la souplesse nécessaire pour les Debian/Ubuntu/Raspberry Pi classiques et les cas Proxmox où vous voudrez parfois verrouiller davantage les comportements de dépôt. citeturn21search0turn29search0
|
||
|
||
Pour Docker, la sémantique officielle est claire : `docker compose pull` récupère les images des services, `docker compose up` peut être relancé en détaché avec `--remove-orphans`, et `docker image prune` / `docker system prune` suppriment certaines ressources inutilisées. Le très bon enseignement de `nas-ops` est qu’il ne faut pas seulement “tirer une commande Docker”, mais d’abord **détecter** les updates, **identifier la stack**, puis **retourner un JSON compact**. En revanche, `nas-ops` s’appuie principalement sur les labels des conteneurs déjà en cours d’exécution (`com.docker.compose.project.working_dir`) ; pour votre app, comme vous voulez déclarer les dossiers Docker depuis le frontend, j’ajouterais un **fallback par scan de répertoires configurés** pour lister les stacks même si aucun conteneur n’est encore lancé. citeturn21search2turn32search1turn32search4turn4view1turn4view2
|
||
|
||
Le contrat JSON doit devenir votre **langage commun** entre scripts distants, backend, frontend, serveur MCP et agent. Je vous propose deux messages canoniques : un **snapshot de disponibilité** et un **résultat d’exécution**. Ce choix est directement inspiré par le fait que `nas-ops` renvoie déjà des JSON structurés, tandis que `linux-update-dashboard` sait piloter des opérations longues et diffuser un flux live séparé. citeturn3view3turn3view4turn4view0turn4view1turn4view2turn12view1
|
||
|
||
```json
|
||
{
|
||
"machineId": "pve-01",
|
||
"hostname": "192.168.1.20",
|
||
"os": {
|
||
"family": "proxmox",
|
||
"version": "8.x"
|
||
},
|
||
"checkedAt": "2026-06-04T12:00:00Z",
|
||
"status": "updates_available",
|
||
"apt": {
|
||
"enabled": true,
|
||
"count": 12,
|
||
"rebootRequired": false,
|
||
"packages": [
|
||
{
|
||
"name": "pve-manager",
|
||
"currentVersion": "8.4-1",
|
||
"targetVersion": "8.4-3",
|
||
"origin": "pve-no-subscription",
|
||
"severityHint": "normal"
|
||
}
|
||
]
|
||
},
|
||
"docker": {
|
||
"enabled": true,
|
||
"count": 2,
|
||
"stacks": [
|
||
{
|
||
"name": "media",
|
||
"path": "/opt/stacks/media",
|
||
"containers": [
|
||
{
|
||
"containerName": "jellyfin",
|
||
"image": "jellyfin/jellyfin:latest",
|
||
"currentImageId": "sha256:aaa",
|
||
"targetImageId": "sha256:bbb",
|
||
"currentVersion": "10.10.0",
|
||
"targetVersion": "10.10.1",
|
||
"sourceUrl": "https://github.com/jellyfin/jellyfin"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
"rawHints": {
|
||
"logImportantLines": [
|
||
"Inst pve-manager [8.4-1] (8.4-3 ...)",
|
||
"Downloaded newer image for jellyfin/jellyfin:latest"
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"executionId": "exec_20260604_001245",
|
||
"machineId": "pve-01",
|
||
"startedAt": "2026-06-04T12:12:45Z",
|
||
"finishedAt": "2026-06-04T12:19:10Z",
|
||
"mode": "manual",
|
||
"actions": [
|
||
{
|
||
"type": "apt_full_upgrade",
|
||
"status": "ok",
|
||
"changes": [
|
||
{
|
||
"name": "pve-manager",
|
||
"from": "8.4-1",
|
||
"to": "8.4-3"
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"type": "docker_up_stack",
|
||
"stack": "media",
|
||
"status": "warning",
|
||
"changes": [
|
||
{
|
||
"containerName": "jellyfin",
|
||
"fromImageId": "sha256:aaa",
|
||
"toImageId": "sha256:bbb"
|
||
}
|
||
],
|
||
"errors": [
|
||
"Found orphan containers; removed with --remove-orphans"
|
||
]
|
||
}
|
||
],
|
||
"rebootRequiredAfterRun": true,
|
||
"importantLogLines": [
|
||
"Setting up pve-manager (8.4-3)",
|
||
"jellyfin Recreated",
|
||
"jellyfin Started"
|
||
],
|
||
"rawLogRef": "reports/2026/06/04/pve-01/exec_20260604_001245.log",
|
||
"reportRef": "reports/2026/06/04/pve-01/exec_20260604_001245.md"
|
||
}
|
||
```
|
||
|
||
Pour éviter les doublons côté agent, je recommande une déduplication par **empreinte fonctionnelle**. Pour les paquets système : `os_family + package_name + current_version + target_version + origin`. Pour Docker : `image_ref + current_digest + target_digest` ou, à défaut, `image + oldImageId + newImageId`. Cela permettra à l’agent de mutualiser les recherches web et de générer un seul résumé par mise à jour identique, même si elle apparaît sur plusieurs machines. Le dépôt `nas-ops` vous aide déjà en exposant des IDs d’image et, quand ils existent, des labels de version et de source. citeturn4view1turn28search2turn28search14
|
||
|
||
Enfin, pour **limiter les tokens transmis à l’agent**, il faut intercaler une étape de **réduction déterministe** avant tout appel LLM. Conservez seulement les lignes utiles : côté APT, les `Inst`, `Conf`, `Remv`, `Err`, `E:`, `W:`, `dpkg:`, `reboot-required`; côté Docker, les `Pulling`, `Digest`, `Status`, `Downloaded newer image`, `Recreating`, `Started`, `Error`. Le reste doit rester dans le log brut archivé, pas dans le prompt. C’est très cohérent avec l’esprit “JSON + flux live séparé” visible dans les deux dépôts. citeturn3view3turn3view4turn4view0turn4view1turn12view1
|
||
|
||
## Frontend et expérience opérateur
|
||
|
||
L’UX que vous décrivez est réaliste et même très bonne pour l’usage homelab/prod légère : une **page d’accueil vide**, un bouton **`+`** pour ajouter une machine, puis des **tuiles** avec **nom**, **IP**, **OS**, **compteurs de mises à jour**, **liste des paquets à mettre à jour**, **liste des stacks ou conteneurs Docker concernés**, et des actions rapides distinctes pour **refresh**, **upgrade**, **docker pull**, **docker apply**, **clean**, **autoremove** et **reboot**. L’exemple visuel de `linux-update-dashboard` montre déjà bien l’intérêt d’un dashboard à tuiles, complété par une page détail plus riche. citeturn24view1turn15search0
|
||
|
||
Je verrais trois écrans majeurs. Le premier est **Dashboard**, centré sur les tuiles machines et l’état global. Le deuxième est **Machine Detail**, avec historique d’activité, templates appliqués, overrides, variables de contexte, packages, stacks Docker et rapports. Le troisième est **Paramétrage**, où vous gérez les préférences frontend, les credentials, les templates de commandes, les profils d’OS, les politiques d’approbation et les réglages d’agent/MCP. Cette séparation reste proche du dépôt de référence tout en étant plus ciblée sur votre périmètre Debian/Ubuntu/Proxmox/RPi + Docker Compose. citeturn24view1turn8view2
|
||
|
||
Pour le **volet terminal à droite**, oui, c’est totalement possible, et même recommandé. Techniquement, le plus propre est un layout **Resizable** avec une liste/tuiles à gauche et un panneau **xterm.js** à droite, alimenté par un **WebSocket**. xterm.js supporte les séquences de terminal et dispose d’un addon d’attache à WebSocket ; de plus, `linux-update-dashboard` expose déjà un flux WebSocket structuré de sortie de commande. Comme l’API navigateur WebSocket ne gère pas la rétropression toute seule, je recommanderais un buffer circulaire côté serveur, la limitation de débit au niveau worker et une compression logique des chunks plutôt qu’un caractère-par-caractère. citeturn20search1turn19search1turn19search12turn7view0turn12view1turn14search8
|
||
|
||
Pour la coloration, il faut distinguer deux besoins. Le **terminal live** utilisera les **couleurs ANSI** et le thème xterm.js. Le **template editor** et la **vue diff avant exécution** utiliseront **Monaco** ou à la limite `highlight.js` pour afficher joliment les scripts. Ce duo vous donne à la fois une expérience terminal crédible et une expérience d’édition moderne. Le dépôt de référence inclut déjà `highlight.js`, ce qui confirme que la brique “rendu coloré” est déjà dans son ADN. citeturn39view4turn19search6turn19search1
|
||
|
||
## Agent IA, skill Hermes et serveur MCP
|
||
|
||
Pour la partie agent, je recommande de **ne pas mélanger l’orchestration de mise à jour et l’intelligence de synthèse**. L’application web doit rester le **source of truth opérationnel**. Au-dessus, vous ajoutez un **serveur MCP** qui expose proprement les machines, les snapshots JSON, les exécutions, les rapports, les templates et les actions autorisées. MCP est conçu pour relier des applications LLM à des outils externes, avec JSON-RPC et des transports standards comme **stdio** et **Streamable HTTP**. C’est exactement le bon niveau d’abstraction pour faire consommer votre plateforme par Claude Code, Hermes ou d’autres agents. citeturn16search3turn16search5turn34search1turn16search7
|
||
|
||
Pour **Claude Code**, je vous conseille un transport **stdio local** en priorité, car il est explicitement recommandé lorsque c’est possible, et c’est le cas d’usage le plus naturel pour un outil qui tourne au plus près du code et des commandes locales. Pour **Hermes Agent**, l’intérêt est double : sa documentation explique qu’il sait se connecter à des serveurs MCP externes, et ses références montrent aussi qu’il a tout un système de skills. Autrement dit, votre architecture peut être : **webapp/API** → **MCP server** → **Claude Code / Hermes**. citeturn34search1turn35search1turn35search20
|
||
|
||
Concernant **Hermes**, les docs officielles disent qu’une **Skill** est le bon format quand la capacité peut être exprimée comme un mélange d’**instructions, commandes shell et outils existants**, alors qu’un **Tool** est préférable pour les intégrations plus profondes, streaming temps réel, auth complexe ou logique très spécialisée. Dans votre cas, la **mise à jour distante elle-même** doit rester dans l’application et son MCP server, mais la **planification, l’analyse des updates, la mutualisation des recherches web, l’interprétation des erreurs et la rédaction de rapports** sont d’excellents candidats pour une **Skill Hermes**. citeturn36search2turn35search2turn35search6
|
||
|
||
Le rôle de l’agent doit rester borné. Je proposerais qu’il sache : lire un snapshot JSON machine, regrouper les updates identiques, rechercher brièvement la nature des paquets ou images quand ils sont inconnus, proposer un **plan de mise à jour succinct**, déclencher uniquement des actions autorisées, puis archiver un **rapport Markdown**. Il ne doit **jamais** recevoir les mots de passe, ni exécuter directement des commandes SSH brutes, ni modifier les templates sans validation opérateur. Cette séparation est cohérente avec les recommandations de Claude Code sur les fichiers `CLAUDE.md`, les patterns d’instructions persistantes et les bonnes pratiques de skills concises, chargées à la demande. citeturn33search5turn33search6turn33search4
|
||
|
||
Les outils MCP que je proposerais en v1 sont très peu nombreux : `list_machines`, `get_machine_snapshot`, `get_execution_report`, `run_refresh`, `run_approved_action`, `list_templates`, `render_template_preview`, `search_archived_reports`. Plus vous garderez cette surface petite, plus les agents resteront fiables. Le skill Hermes, lui, agira surtout comme un **mode d’emploi intelligent** de ce MCP, pas comme un exécuteur système autonome. citeturn35search1turn36search2
|
||
|
||
## Fichiers de consigne proposés
|
||
|
||
Les docs officielles de Claude Code indiquent que les fichiers **`CLAUDE.md`** servent d’instructions persistantes de projet, lues au démarrage de chaque session. Les docs Hermes indiquent de leur côté qu’une skill repose sur un **`SKILL.md`** avec frontmatter et sections explicites (`When to Use`, `Procedure`, `Verification`, etc.). Je vous propose donc de partir avec **un fichier principal de consignes pour Claude Code**, **une skill Hermes**, et **une note de contrat MCP**. citeturn33search5turn36search2turn36search0
|
||
|
||
### `CLAUDE.md`
|
||
|
||
```md
|
||
# Projet webapp de mise à jour distante Linux
|
||
|
||
## Langue et ton
|
||
- Répondre en français.
|
||
- Favoriser des propositions concrètes, structurées et justifiées.
|
||
- Ne pas imposer une architecture finale : proposer, comparer, argumenter.
|
||
|
||
## Rôle de l’agent
|
||
- L’agent aide à concevoir et faire évoluer la webapp.
|
||
- L’agent ne décide pas seul de la structure finale du projet.
|
||
- L’agent doit expliciter les compromis techniques importants.
|
||
- L’agent doit prioriser la sécurité, la lisibilité et l’évolutivité.
|
||
|
||
## Références locales
|
||
- Les dépôts `linux-update-dashboard` et `nas-ops` sont présents comme références de travail.
|
||
- Les considérer comme des sources d’inspiration et d’analyse.
|
||
- Ne pas recopier de larges portions de code sans vérifier la compatibilité de licence et sans réécriture adaptée au projet.
|
||
|
||
## Objectif produit
|
||
Construire une webapp qui permet :
|
||
- d’ajouter des machines sans inventaire prédéfini ;
|
||
- de rafraîchir les mises à jour en tâche de fond ;
|
||
- de déclencher manuellement les upgrades système et Docker ;
|
||
- de gérer Debian, Ubuntu, Proxmox et Raspberry Pi OS en priorité ;
|
||
- de configurer apt-cacher-ng depuis le frontend ;
|
||
- de gérer des templates de commandes et des overrides par machine ;
|
||
- d’exposer des JSON propres au frontend, au MCP server et aux agents ;
|
||
- d’archiver des rapports Markdown après chaque exécution ;
|
||
- de préparer l’évolution vers post-install, réseau, installation de paquets, scripts custom.
|
||
|
||
## Principes d’architecture
|
||
- Backend TypeScript modulaire.
|
||
- Frontend React + TypeScript.
|
||
- Les opérations distantes passent par SSH agentless.
|
||
- La logique métier d’update doit vivre dans des templates shell versionnés.
|
||
- Le backend orchestre, journalise, valide, chiffre, historise.
|
||
- Les secrets ne vont jamais dans les prompts des agents.
|
||
- Les logs destinés aux agents doivent être filtrés et résumés.
|
||
|
||
## Sécurité
|
||
- Préférer la vérification des host keys.
|
||
- Prévoir bastion / ProxyJump.
|
||
- Utiliser un utilisateur dédié côté machines.
|
||
- Réduire sudo au strict nécessaire.
|
||
- Chiffrer les credentials au repos.
|
||
- Ne jamais afficher de secret brut dans les logs, l’UI ou les retours MCP.
|
||
|
||
## Règles fonctionnelles
|
||
- `update/check` = tâche de fond.
|
||
- `upgrade/full-upgrade/dist-upgrade/docker apply/reboot` = action manuelle validée.
|
||
- Toujours distinguer détection, planification et exécution.
|
||
- Toujours produire un JSON canonique par machine.
|
||
- Toujours archiver un rapport `.md` après exécution.
|
||
|
||
## UX attendue
|
||
- Page d’accueil vide au premier lancement.
|
||
- Bouton `+` pour ajouter une machine.
|
||
- Tuiles machines avec nom, IP, OS, compteurs, paquets et Docker à mettre à jour.
|
||
- Onglet Paramétrage pour templates, règles frontend, profils d’OS et options agent.
|
||
- Volet terminal à droite, redimensionnable, avec flux live.
|
||
|
||
## Contraintes agent
|
||
- Lorsqu’une proposition d’architecture est faite, toujours distinguer :
|
||
- MVP recommandé ;
|
||
- options alternatives ;
|
||
- risques / limites.
|
||
- Éviter les générateurs opaques et les abstractions inutiles.
|
||
- Préférer des noms de dossiers explicites et une convention stable.
|
||
|
||
## Livrables attendus
|
||
Quand on demande une proposition technique, fournir si pertinent :
|
||
- structure de projet ;
|
||
- schémas JSON ;
|
||
- contrats d’API ;
|
||
- templates shell ;
|
||
- plan de roadmap ;
|
||
- risques de sécurité ;
|
||
- stratégie de tests ;
|
||
- impacts UX.
|
||
```
|
||
|
||
### `hermes-skills/update-ops-planner/SKILL.md`
|
||
|
||
```md
|
||
---
|
||
name: update-ops-planner
|
||
description: Analyse les snapshots JSON de mises à jour Linux/Docker, déduplique les items, recherche brièvement leur objet, propose un plan d’exécution et génère un rapport Markdown.
|
||
version: 1.0.0
|
||
author: Projet Webapp Updates
|
||
license: Proprietary
|
||
platforms: [linux]
|
||
metadata:
|
||
hermes:
|
||
tags: [ops, updates, linux, docker, mcp, reporting]
|
||
requires_toolsets: [web]
|
||
related_skills: []
|
||
---
|
||
|
||
# Update Ops Planner
|
||
|
||
## When to Use
|
||
Charger cette skill quand l’utilisateur :
|
||
- fournit un snapshot JSON machine de mises à jour ;
|
||
- demande un plan de mise à jour système ou Docker ;
|
||
- demande une synthèse d’erreurs d’upgrade ;
|
||
- demande un rapport Markdown à archiver ;
|
||
- demande de mutualiser l’analyse sur plusieurs machines.
|
||
|
||
## Quick Reference
|
||
- Toujours lire d’abord le JSON normalisé.
|
||
- Dédupliquer les mises à jour identiques entre machines.
|
||
- Chercher sur le web uniquement pour :
|
||
- paquets ou images peu explicites ;
|
||
- composants critiques ;
|
||
- erreurs non triviales ;
|
||
- changements majeurs de version.
|
||
- Produire une réponse courte, opérationnelle et non alarmiste.
|
||
- Ne jamais demander ni afficher de secrets.
|
||
|
||
## Procedure
|
||
1. Lire le snapshot JSON fourni par le MCP server.
|
||
2. Identifier :
|
||
- updates système ;
|
||
- updates Docker ;
|
||
- reboot requis ;
|
||
- erreurs ou warnings ;
|
||
- éléments dupliqués entre machines.
|
||
3. Pour chaque item pertinent, dédupliquer par nom + version cible.
|
||
4. Pour les items inconnus ou importants, faire une recherche web brève.
|
||
5. Produire un plan de mise à jour :
|
||
- ordre recommandé ;
|
||
- éléments sûrs à appliquer en lot ;
|
||
- éléments à isoler ;
|
||
- reboot éventuel ;
|
||
- vérifications post-run.
|
||
6. Générer un rapport Markdown avec :
|
||
- résumé exécutif ;
|
||
- tableau des updates système ;
|
||
- tableau des updates Docker ;
|
||
- risques connus ;
|
||
- ordre recommandé ;
|
||
- incidents / erreurs ;
|
||
- annexes JSON utiles.
|
||
7. Si un résultat d’exécution est fourni, comparer avant/après et résumer :
|
||
- succès ;
|
||
- écarts de versions ;
|
||
- erreurs restantes ;
|
||
- actions de remédiation.
|
||
|
||
## Pitfalls
|
||
- Ne pas inférer une criticité sans source.
|
||
- Ne pas répéter la même recherche pour 10 machines identiques.
|
||
- Ne pas transmettre les logs bruts complets au modèle si une version filtrée existe.
|
||
- Ne pas confondre `refresh` et `upgrade`.
|
||
- Ne pas supposer qu’un `docker pull` implique un redéploiement réussi.
|
||
|
||
## Verification
|
||
Vérifier que la sortie contient :
|
||
- un résumé lisible ;
|
||
- un plan d’exécution ordonné ;
|
||
- la liste des mises à jour regroupées ;
|
||
- une section risques/erreurs si nécessaire ;
|
||
- un rapport Markdown archivable.
|
||
|
||
## Output Format
|
||
Toujours retourner :
|
||
- `summary`
|
||
- `deduplicated_updates`
|
||
- `recommended_plan`
|
||
- `web_notes`
|
||
- `report_markdown`
|
||
```
|
||
|
||
### `docs/MCP_CONTRACTS.md`
|
||
|
||
```md
|
||
# Contrats MCP proposés
|
||
|
||
## Principe
|
||
Le MCP server ne contient pas la logique SSH métier.
|
||
Il appelle l’API interne de la webapp et expose un outillage minimal, stable et typé.
|
||
|
||
## Tools v1
|
||
- `list_machines()`
|
||
- `get_machine_snapshot(machineId)`
|
||
- `get_machine_execution(machineId, executionId)`
|
||
- `run_refresh(machineId)`
|
||
- `run_action(machineId, action, options)`
|
||
- `list_templates()`
|
||
- `preview_template(machineId, templateName)`
|
||
- `search_reports(query)`
|
||
|
||
## Resources v1
|
||
- `machine://{id}/snapshot`
|
||
- `machine://{id}/history`
|
||
- `report://{executionId}`
|
||
|
||
## Règles
|
||
- Les tools d’exécution ne reçoivent jamais de secret brut.
|
||
- Les réponses d’exécution renvoient des références de rapport et des lignes importantes, pas uniquement du log brut.
|
||
- Le MCP server doit rester une façade de l’API métier.
|
||
- Les actions destructives doivent être explicitement approuvées côté application.
|
||
|
||
## JSON canoniques
|
||
Utiliser deux schémas principaux :
|
||
- `update availability snapshot`
|
||
- `execution result`
|
||
|
||
## Déduplication
|
||
- Système : `os_family + package + from + to + origin`
|
||
- Docker : `image + fromDigest + toDigest`
|
||
```
|
||
|
||
## Questions ouvertes et limites
|
||
|
||
Le point le plus important à clarifier avant toute implémentation est **le niveau de réutilisation autorisé** des deux dépôts, car `linux-update-dashboard` est bien sous **AGPL-3.0**, tandis que la licence de `nas-ops` n’était pas visible dans les pages consultées. Tant que ce point n’est pas purgé, je vous conseille de garder les deux dépôts dans le projet comme **références lues par les agents et les développeurs**, mais de reconstruire votre propre code. citeturn24view1turn24view0turn24view2
|
||
|
||
Il reste aussi quelques choix d’architecture à trancher au début du projet : **PostgreSQL seul avec pg-boss** ou **PostgreSQL + Redis/BullMQ**, **auth par mot de passe en production** ou **SSH key only**, **scope strictement agentless** ou **préparation d’un futur agent local**, et **politique exacte de stockage des rapports** sur filesystem local, NAS ou bucket objet. Ces questions ne bloquent pas l’étude, mais elles influencent la structure du backend et le niveau de complexité du déploiement. citeturn27search2turn27search5turn27search0turn27search12
|
||
|
||
En synthèse, la proposition la plus robuste pour votre besoin est : **webapp React/TypeScript avec design system shadcn/ui + icônes Lucide + terminal xterm.js**, **backend Hono/TypeScript**, **orchestration SSH agentless**, **templates shell versionnés par OS et par capacité**, **JSON canoniques pour frontend/MCP/agent**, **refresh en tâche de fond**, **upgrade manuel**, **serveur MCP en façade**, et **skill Hermes centrée sur l’analyse, la déduplication, la recherche web ciblée et le reporting**. C’est la combinaison la plus cohérente de ce que vos deux dépôts de référence font déjà bien, tout en restant propre, évolutive et compatible avec une future collaboration structurée avec Claude Code. citeturn24view1turn3view3turn4view1turn20search13turn26search1turn19search1turn25search4turn34search1turn36search2 |