# Dossier technique pour le deuxième widget Glance de monitoring d’agents Rust ## Conclusion opérationnelle Oui, ton deuxième widget est **réalisable**, et il est même cohérent avec l’architecture de Glance à condition de le traiter comme **une application externe intégrée dans Glance**, pas comme un widget natif compilé “dans” Glance en Rust. Le dépôt principal de Glance est un projet **Go** avec un `go.mod`, et sa mécanique de widgets personnalisés repose aujourd’hui sur quatre portes d’entrée documentées par le projet lui-même : `iframe`, `html`, `extension` et `custom-api`. Glance précise aussi que les pages **ne se mettent pas à jour automatiquement en arrière-plan** et qu’un rechargement de page est normalement nécessaire pour récupérer les nouvelles données. Pour ton besoin — tuiles live, popup latéral, auto-découverte, installation d’agent, mise à jour, métriques à fréquences différentes et états de veille/arrêt/actif — l’option la plus robuste est donc : **backend Rust + petite UI web servie par ce backend + intégration dans Glance via `iframe`**. citeturn36view0turn33view1turn35view0turn34view0 Il y a aussi une nuance importante de vocabulaire : le projet étudié ici est **Glance**, pas **Glances**. La documentation de Glance dit d’ailleurs explicitement que le widget `server-stats` est encore “under development”, qu’il peut s’appuyer sur le **Glance Agent** pour des serveurs distants, et que le support d’autres providers “such as Glances” viendra plus tard. Autrement dit, ton besoin avancé dépasse clairement ce que le widget natif `server-stats` couvre aujourd’hui. citeturn3view0 Mon verdict est donc simple : **oui pour Rust**, mais **en service externe** consommé par Glance, pas comme module interne du binaire Go de Glance. Si tu veux un rendu vraiment vivant et administrable, je te recommande de bâtir un petit produit séparé, par exemple `fleet-collector` et `fleet-agent`, puis de l’afficher dans une page Glance dédiée. Cette approche servira d’ailleurs très bien ton **premier widget IP réseau** et ton **deuxième widget agents** avec un même modèle d’inventaire. citeturn33view1turn34view0turn31view0turn35view0 ## Ce que Glance permet réellement pour ce besoin Glance organise l’interface autour de **pages** et de **colonnes**. Une page peut jouer le rôle de “tab” au sens fonctionnel, avec un layout `wide` et des colonnes `full` ou `small`. Si tu veux réserver un onglet complet à ton monitoring, Glance le permet nativement côté configuration, sans toucher au code source du projet. citeturn3view0 Pour ton cas, les trois mécanismes utiles sont les suivants. D’abord, `custom-api` sait appeler une API JSON avec `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, gérer des headers, un body, des `subrequests`, puis rendre le résultat avec un template basé sur **Go `html/template`** et `gjson`. C’est puissant pour des widgets “server-rendered” relativement riches, mais la doc de Glance dit aussi explicitement que ce mode demande des notions de programmation, HTML, CSS et templates Go. Ensuite, `extension` est un protocole HTTP simple où un service externe renvoie du HTML et quelques headers spécifiques ; Glance documente néanmoins que cette API est **work in progress**, que le cache par défaut d’une extension est de **30 minutes**, qu’il est configurable, et que le seul type de contenu supporté actuellement est `html`. Enfin, `iframe` embarque simplement une application externe à partir d’une URL et d’une hauteur donnée. Pour un écran de supervision avec popups, tuiles dynamiques, tri intelligent, interaction fine et mises à jour live, `iframe` est de loin la meilleure option. citeturn35view0turn31view0turn34view0turn33view1 La principale raison est le modèle d’actualisation de Glance. Le README officiel dit que les widgets ne font **pas de requêtes périodiques en arrière-plan** et que les données sont récupérées au chargement puis mises en cache ; il faut normalement recharger la page pour voir les changements. Il existe bien une pull request ouverte ajoutant des “live-events” avec **polling serveur toutes les 15 secondes** et SSE pour `monitor` et `custom-api`, mais elle est **encore ouverte** au 19 mai 2026 et n’appartient donc pas au comportement stable documenté du `main` actuel. Ton besoin de CPU/RAM à 1 seconde ne doit pas dépendre d’une PR non mergée. citeturn33view1turn33view0 Glance reste toutefois très bon comme **conteneur**, car tu peux lui faire servir des assets via `assets-path`, injecter du HTML dans le `` avec `document.head`, ajouter un `custom-css-file`, et cibler les widgets par `widget-type-*` ou `css-class`. Autrement dit, même si le vrai widget vit dans un service Rust séparé, tu peux le “fondre” visuellement dans l’univers Glance. citeturn32view0turn32view1turn32view2turn32view3 Un squelette de configuration Glance pour ce deuxième widget ressemblerait à ceci : ```yaml pages: - name: Infrastructure width: wide columns: - size: full widgets: - type: iframe title: Fleet Monitor source: https://dash.example.net/widgets/agents height: 820 css-class: fleet-monitor ``` Si tu veux garder un mode “dégradé” purement Glance, tu peux aussi fournir un **petit widget `custom-api`** qui n’affiche qu’un résumé global : nombre d’agents, nombre offline, charge CPU moyenne, machines en alerte, lien d’installation et lien “ouvrir la vue détaillée”. Mais la vue principale riche doit rester dans l’`iframe`. citeturn35view0turn34view0turn32view3 ## Architecture recommandée pour le widget agents Je te recommande une architecture en trois briques. **Première brique** : un `fleet-agent` Rust installé sur chaque VM ou machine physique. **Deuxième brique** : un `fleet-collector` Rust central qui reçoit les enregistrements, stocke l’inventaire, expose une API et un flux temps réel. **Troisième brique** : une UI web légère, servie par le collector, affichée dans Glance via `iframe`. Cette séparation colle au modèle de Glance, contourne proprement l’absence d’auto-refresh généralisé, et te laisse toute latitude pour le placement intelligent des tuiles, les popups, les liens personnalisés et les workflows d’installation/mise à jour. citeturn33view1turn34view0turn35view0turn31view0 ```text [ VM / Bare metal ] └─ fleet-agent (Rust) ├─ collecte CPU / RAM / disque / process ├─ inventaire matériel ├─ écoute veille / reboot / shutdown └─ push JSON vers collector [ Serveur central ] └─ fleet-collector (Rust) ├─ API d’enregistrement des agents ├─ stockage inventaire + séries courtes ├─ stream SSE / WebSocket pour l’UI ├─ génération commandes install / update └─ UI web du widget [ Glance ] └─ page "Infrastructure" └─ widget iframe -> UI du collector ``` Je te conseille un modèle **push** depuis les agents vers le collector, parce que ton cahier des charges inclut des événements qui se prêtent mal à un modèle purement pull : arrêt propre, passage en veille, reprise, changement d’état, heartbeat, auto-enregistrement au boot, et publication de métriques avec des fréquences hétérogènes. Comme Glance n’est pas un moteur de polling continu des widgets, et comme `server-stats` est encore explicitement en développement, vouloir faire ça directement “dans Glance” te mettrait rapidement à contre-courant du produit. citeturn33view1turn3view0 La bonne granularité, à mon sens, est la suivante. **Inventaire statique** au démarrage puis deux fois par jour : hostname, OS, carte mère, modèle, vendor, serial, interfaces réseau, icône, emplacement, groupe, parent, liens. **Métriques rapides** toutes les 1 à 5 secondes : CPU, RAM, état de disponibilité, éventuellement charge GPU si la machine en a une. **Métriques lentes** toutes les 30 minutes : capacité disque, occupation des volumes, état SMART synthétique. **Événements** à la volée : démarrage, arrêt, veille, reprise, changement d’IP, agent obsolète, échec d’un module. Cette séparation colle bien aux capacités des APIs système disponibles et évite de transmettre trop de bruit. citeturn22view0turn24search7turn8search0turn8search4turn12search0turn11search0turn10search0turn30search6 Pour le **volet secondaire** de tes tuiles, je te conseille de ne pas tout remonter à 1 seconde. Les données de popup — SMBIOS/DMI, services, icône, emplacement, relations de parenté, liste des top processus, état détaillé des disques — peuvent être stockées côté collector et réactualisées plus lentement, voire à la demande. Les docs de `sysinfo` montrent explicitement que le CPU s’appuie sur des différences temporelles, qu’il faut garder une même instance `System`, et qu’il vaut mieux utiliser des refresh ciblés pour les performances. Cette philosophie va exactement dans le sens de ton besoin “fréquences différentes selon les familles de données”. citeturn22view0turn26view0 ## Stack Rust recommandée Pour le **runtime** et l’API réseau, la combinaison la plus naturelle est **Tokio + Axum**. Tokio est le runtime asynchrone standard de fait pour les applications réseau Rust ; sa primitive `interval` est adaptée à des boucles planifiées de collecte. Axum est conçu pour fonctionner avec Tokio et Hyper, sait gérer des APIs HTTP, les WebSockets, et fournit nativement des réponses **SSE**. Pour ton widget, SSE suffit souvent très bien : le collector pousse les changements de tuiles, et l’UI met à jour la grille sans refresh. citeturn16search22turn20search2turn20search5turn16search2turn20search0turn20search4 Pour la **collecte système générale**, je recommande **`sysinfo`** comme base. La doc officielle montre que la crate couvre processus, mémoire, CPU, disques, réseaux, températures de composants, et qu’elle dispose de `RefreshKind`, `ProcessRefreshKind`, `DiskRefreshKind`, ainsi que d’un `MINIMUM_CPU_UPDATE_INTERVAL`. Elle expose aussi des informations de carte mère, par exemple nom, vendor, version, numéro de série et asset tag via `Motherboard`. Pour les top processus, elle fournit notamment `cpu_usage()`, `memory()`, `status()`, `parent()`, `exe()` et `cmd()`. C’est une excellente base pour un agent Rust Linux-first. citeturn22view0turn25view0turn25view1turn26view0turn26view1 Pour le **matériel** et les identifiants, tu as deux étages. D’abord `sysinfo`, qui sait remonter une partie de l’inventaire matériel. Ensuite, pour les informations plus complètes ou plus “admin” — SMBIOS/DMI, châssis, BIOS, vendor, références — tu peux compléter avec `dmidecode` ou directement les fichiers sysfs. La doc de `dmidecode` rappelle toutefois que ces informations viennent du firmware SMBIOS/DMI et peuvent être **rapides et sûres à lire** mais **parfois peu fiables**, car elles dépendent de ce que le firmware expose. C’est important pour ton design : il faut afficher ces champs comme de l’inventaire déclaratif, pas comme une vérité absolue. citeturn25view0turn9search12turn9search6 Pour les **disques**, je te conseille de séparer “occupation des volumes” et “santé physique”. L’occupation se fait très bien avec `sysinfo::Disks`. La santé disque et les attributs SMART doivent venir de **`smartctl`**, car la documentation officielle indique que `smartctl` contrôle et surveille le système SMART des disques ATA/SATA, SCSI/SAS et SSD, et son code source expose un mode `--json`/`-j` pour une sortie JSON ou YAML. C’est le meilleur compromis Rust aujourd’hui : exécuter `smartctl`, parser le JSON, et normaliser un petit résumé par disque pour ton popup ou tes alertes. citeturn24search7turn8search0turn8search4 Pour les **GPU**, je te recommande un design par provider optionnel. Côté NVIDIA, la référence est clairement **NVML** ; NVIDIA documente NVML comme une API C de monitoring et management des GPU, thread-safe, et base de `nvidia-smi`. En Rust, `nvml-wrapper` permet d’y accéder proprement. Côté AMD, la doc ROCm indique que **AMD SMI** est l’interface unifiée vers laquelle AMD pousse désormais les usages de monitoring/management GPU et qu’elle est la successeure de ROCm SMI. Traduction pratique : **phase 1 NVIDIA si tu veux aller vite**, **phase 2 AMD SMI si ton parc le justifie**. citeturn10search0turn10search1turn6search4turn30search1turn30search6 Pour la **base de données centrale**, tu as deux bons choix. Si ton parc reste modeste et si tu veux un déploiement très simple, **SQLite en WAL** est parfaitement défendable avec un **collector unique** et un modèle “une écriture logique à la fois”. La doc SQLite rappelle comment activer WAL et souligne que ce mode permet, en règle générale, que les écrivains ne bloquent pas les lecteurs et inversement, tout en expliquant le rôle des checkpoints. En revanche, la doc SQLite signale aussi un **bug rare WAL-reset** corrigé en **3.51.3** le 13 mars 2026, ainsi que dans certains backports. Donc si tu pars sur SQLite/WAL, veille à utiliser une version corrigée. Si tu veux monter en charge, multi-utilisateur, ou faire de la rétention longue avec analytics, **PostgreSQL** devient plus confortable. Côté Rust, **SQLx** est adapté aux deux mondes : crate async, support Tokio, support SQLite/PostgreSQL, requêtes vérifiables à la compilation. citeturn27view0turn19search0turn19search1 Pour les **mises à jour de l’agent**, deux options sont sérieuses. Soit tu fais du **self-update** in-place avec une crate dédiée ; la doc de `self_update` précise qu’elle sait mettre à jour des exécutables Rust en place à partir de plusieurs backends de distribution. Soit, ce que je préfère dans ton contexte, tu gardes un **installeur shell** et des **binaires versionnés**, ce qui te donne un chemin d’upgrade plus transparent, plus compatible avec systemd et plus facile à signer/vérifier. Pour la signature, **Minisign** est une option légère et robuste, officiellement documentée comme un outil simple de signature/vérification basé sur **Ed25519**. citeturn16search1turn17search0 ## Déploiement, découverte et mises à jour Pour la **distribution** des artefacts, Gitea est suffisant. La documentation officielle du **Generic Package Registry** explique qu’il peut publier des fichiers génériques comme des binaires de release, via `PUT`, et les télécharger via `GET` sur des URLs versionnées. Gitea documente aussi l’authentification API par Basic auth, query token, ou surtout `Authorization: token ...`. Concrètement, tu peux stocker dans Gitea : le binaire par plateforme, le fichier `.minisig`, un `manifest.json`, et le `install.sh`. citeturn28view0turn28view2 Dans ce modèle, ton widget central peut générer la commande d’installation exactement dans l’esprit que tu veux, par exemple : ```bash curl -fsSL https://git.example.net/api/packages/infra/generic/fleet-agent/1.2.0/install.sh \ | bash -s -- \ --collector https://collector.example.net \ --token AGENT_REG_TOKEN \ --group vm \ --icon server \ --link https://proxmox.example.net ``` Je te conseille toutefois que ce script **ne télécharge pas directement un binaire non vérifié** : il doit récupérer le manifest, télécharger l’artefact pour l’OS/arch courants, vérifier la signature Minisign, installer le binaire, créer l’unité systemd et démarrer le service. C’est plus sûr que le simple `curl | bash` aveugle, tout en restant simple à opérer. citeturn28view0turn17search0 Pour l’**auto-découverte**, je te recommande de faire de la **self-registration** d’abord, et du **mDNS/DNS-SD** seulement en option locale. Les RFC IETF disent clairement que **mDNS** fonctionne sur le **local link** en absence de DNS conventionnel, et que **DNS-SD** sert à découvrir les instances d’un service à partir de son type. Cela veut dire que mDNS est pratique sur un LAN plat, mais pas un mécanisme principal pour des segments séparés, des VLANs ou des routes plus complexes. En outre, les travaux IETF sur la privacy de DNS-SD rappellent que la découverte de services divulgue typiquement des noms d’hôtes et des paramètres réseau. En pratique : **annonce mDNS facultative pour le confort local**, **enregistrement explicite au collector pour le vrai fonctionnement**. citeturn14search3turn14search1turn14search7 Pour la **détection des états système**, le plus propre est de mélanger hooks systemd et écoute D-Bus. La documentation systemd indique que `ExecStop=` et `ExecStopPost=` s’exécutent lors d’une opération d’arrêt/restart du service, ce qui te donne un bon point de sortie “propre” pour publier un dernier événement. En parallèle, l’interface `org.freedesktop.login1` documente les signaux `PrepareForShutdown()` et `PrepareForSleep()` envoyés juste avant et juste après extinction/suspend, avec un booléen indiquant l’entrée ou la sortie de l’état. C’est exactement ce qu’il te faut pour les états “veille”, “arrêt”, “actif”. citeturn11search0turn12search0 Enfin, pour la **mise à jour par mini-modules**, le collector peut exposer une notion de “catalogue de modules” par agent : par exemple `smart`, `gpu-nvidia`, `gpu-amd`, `services-extra`, `netinfo-advanced`. Le widget n’a pas besoin d’exécuter lui-même le code distant ; il peut afficher la commande exacte à lancer, ou bien l’agent peut **poller un manifest signé** et proposer/appliquer l’update localement selon une politique. Cette seconde approche est plus sûre et plus industrialisable si tu comptes dépasser quelques machines. citeturn28view0turn16search1turn17search0 ## Plan de réalisation recommandé Je te recommande un **MVP Linux-first** en trois étapes. **Étape une** : collector Rust + UI iframe + agent Rust qui remonte en push l’enregistrement, le heartbeat, CPU, RAM, volumes, état de base, et un top 5 processus rafraîchi toutes les 10 à 15 secondes. Tu obtiens déjà les tuiles, l’état online/offline, les popups secondaires et la génération de commandes d’installation. Cette première étape s’appuie presque entièrement sur `tokio`, `axum`, `sysinfo`, `systemd` et la mécanique Glance `iframe`. citeturn20search2turn16search2turn22view0turn26view0turn34view0 **Étape deux** : enrichissement matériel. Tu ajoutes DMI/SMBIOS, cartes réseau, disques SMART, capteurs/temperatures disponibles, et éventuellement données GPU NVIDIA. Cette couche est surtout utile pour ton volet latéral “hardware, icon, services, lien, emplacement, parent”. C’est aussi l’étape où tu peux stabiliser ton modèle d’inventaire pour qu’il serve **en commun** ton premier widget “cartographie IP réseau” et ton deuxième widget “agents”. citeturn25view0turn9search12turn8search0turn10search0 **Étape trois** : exploitation avancée. Tu ajoutes auto-discovery optionnelle, agent modules, politiques d’update, AMD SMI si nécessaire, et éventuellement une petite vue Glance `custom-api` de synthèse pour les KPIs globaux. À ce stade, Glance reste le portail, mais la logique métier vit clairement dans ton service Rust, ce qui est précisément ce que l’architecture de Glance favorise déjà avec `iframe`, `extension` et `custom-api`. citeturn35view0turn31view0turn34view0 ## Limites et points ouverts La principale limite technique est que ton besoin est **beaucoup plus proche d’un mini-produit de fleet monitoring** que d’un simple widget statique. Ce n’est pas un défaut de Glance ; c’est juste que Glance documente aujourd’hui un modèle de données principalement **fetch-on-load + cache**, avec des extensions HTML et des custom APIs orientées rendu, et non un framework de widgets riches temps réel comparable à une SPA supervisée par un bus d’événements. L’existence d’une PR SSE non mergée va dans ce sens : le besoin de live updates existe, mais il n’est pas encore un socle stable du projet. citeturn33view1turn33view0turn31view0turn35view0 Le deuxième point ouvert est le **périmètre OS**. La stack que je te propose est excellente pour un parc **Linux / VMs / bare metal sous systemd**, parce qu’elle repose sur `smartctl`, login1/systemd, SMBIOS/DMI, NVML/AMD SMI et `sysinfo`. Si tu veux couvrir sérieusement Windows, BSD ou macOS avec le même niveau de détails matériels et d’événements système, il faudra des providers spécifiques ou un niveau de service un peu moins riche. `sysinfo` est multi-OS, mais tout ce qui touche au shutdown propre, à SMART, à SMBIOS détaillé et à la télémétrie GPU dépend fortement de l’OS et du vendor. citeturn22view0turn9search12turn8search0turn12search0turn10search0turn30search6 Le troisième point ouvert est la **taille de parc** visée. Si tu parles de quelques dizaines de machines, SQLite en WAL avec un collector unique, un cache mémoire court et des agrégats récents est probablement suffisant. Si tu parles de centaines de nœuds, d’historiques longs ou de multiples utilisateurs simultanés du dashboard, je partirais plus vite sur PostgreSQL et un découplage plus net entre ingestion temps réel et UI. Et si tu restes sur SQLite, je te recommande explicitement de vérifier la version déployée au regard des correctifs WAL mentionnés par la documentation SQLite de mars 2026. citeturn27view0turn19search1 En synthèse finale : **oui, tu peux ajouter ce widget avec du Rust** ; **non, je ne te recommande pas de vouloir l’ajouter “dans Glance” comme widget natif Rust** ; **oui, je te recommande fortement un service Rust externe affiché par `iframe` dans une page Glance**, avec un agent Rust en push, un collector temps réel, un stockage léger, une distribution via Gitea, et une vérification de signature des binaires. C’est l’architecture la plus propre, la plus maintenable, et la plus fidèle à la manière dont Glance expose aujourd’hui ses points d’extension. citeturn36view0turn35view0turn34view0turn31view0turn33view1