Files
SentinelMesh/doc_brainstorming/deep-research-report(5).md
T
2026-05-19 06:39:32 +02:00

19 KiB
Raw Blame History

Dossier technique sur Glance et lajout dun onglet IP réseau

Périmètre de lanalyse

Le dépôt que tu as donné est bien glanceapp/glance, cest-à-dire Glance, le dashboard self-hosted de lorganisation glanceapp. Ce nest pas Glances de nicolargo, qui est un outil de monitoring système différent. La nuance compte beaucoup pour ton projet, parce que Glance est dabord un framework de tableau de bord configurable en YAML, pensé pour agréger et afficher des données, pas un moteur natif de supervision réseau temps réel. citeturn2view0turn26search2turn19view0

La conséquence la plus importante pour ton besoin est celle-ci : Glance doit être traité comme la couche dinterface, tandis que le scan de ton réseau 10.0.0.0/22 et linventaire détaillé des IP doivent vivre dans un service séparé. Le README officiel précise en effet quune actualisation de page est nécessaire pour mettre à jour les informations et quaucune requête périodique nest effectuée en arrière-plan par les widgets ; les données sont chargées au rendu puis mises en cache suivant la durée définie pour chaque widget. citeturn19view0

Autre point utile : le widget natif monitor existe déjà, mais il est conçu pour une liste de sites/URLs avec title, url, éventuel check-url, icon, timeouts et variantes daffichage. Il ne correspond pas à un inventaire CIDR, à un découpage par sous-réseaux, ni à un volet détaillé par IP. Ton besoin dépasse donc nettement le périmètre des widgets intégrés actuels. citeturn12view2

Langages et technologies employés

Daprès la répartition des langages affichée sur le dépôt GitHub, Glance est majoritairement écrit en Go, avec une part significative de HTML, JavaScript et CSS, puis un peu de Dockerfile. La page du dépôt affiche précisément Go 55,5 %, HTML 16,2 %, JavaScript 15,0 %, CSS 13,2 % et Dockerfile 0,1 %. Cette répartition correspond bien à ce quon voit dans larborescence : un backend Go, des templates HTML embarqués, des assets CSS/JS locaux et un packaging Docker très léger. citeturn19view0

Le cœur applicatif est un module Go nommé github.com/glanceapp/glance, et le go.mod déclare aujourdhui Go 1.24.3. Les dépendances directes sont révélatrices : fsnotify pour la surveillance et le rechargement de config, gofeed pour les flux, gopsutil/v4 pour les métriques système, gjson pour le parsing JSON dans les widgets custom, x/crypto pour lauthentification et yaml.v3 pour toute la configuration. À noter quil y a un léger écart documentaire : le README parle encore dun build avec Go >= 1.23, mais le go.mod du dépôt main pointe déjà sur 1.24.3 ; si tu recompiles, cest le go.mod quil faut considérer comme source de vérité. citeturn4view0turn19view0

Le point dentrée est volontairement minuscule : main.go ne fait quappeler glance.Main() depuis internal/glance. Le Dockerfile confirme le modèle de distribution : build en Go sur Alpine, avec CGO_ENABLED=0, puis copie du binaire dans une image Alpine finale. En pratique, Glance est donc pensé comme un binaire Go autonome, sans runtime Node, sans bundle frontend externe et avec une surface de build très simple. citeturn4view2turn4view1

Le frontend nemploie pas de framework type React/Vue/Svelte. Le README insiste sur du “minimal vanilla JS”, et les guidelines de contribution ajoutent même explicitement “No package.json et la consigne déviter les nouvelles dépendances. Pour un développeur, cest un signal très clair : Glance privilégie du server-side rendering avec templates Go, des assets statiques maison, et un JavaScript utilitaire limité. citeturn19view0

Techniquement, les assets sont embarqués dans le binaire via //go:embed pour static et templates. Le code montre aussi que le CSS est bundlé au runtime à partir de internal/glance/static/css, tandis que internal/glance/static/js contient des scripts comme animations.js, calendar.js, login.js, masonry.js, page.js, popover.js, templating.js, todo.js et utils.js. Côté templates, on trouve les vues page/document/footer ainsi quun template HTML dédié par widget ou style de widget. citeturn9view4turn13view0turn13view1turn14view0

Structure interne de lapplication

Larborescence de haut niveau est sobre et lisible : à la racine, on a surtout docs/, internal/glance/, pkg/sysinfo/, main.go, go.mod, Dockerfile et la config de release. Pour une lecture développeur, tout le produit est concentré dans internal/glance, tandis que docs/ sert de documentation fonctionnelle et pkg/sysinfo/ isole une partie des métriques système. citeturn2view0

La structure de configuration est également très claire dans config.go. On y voit les blocs top-level server, auth, document, theme, branding et surtout pages. Chaque page possède un name, un slug, une largeur (width), des head-widgets et des columns; chaque colonne porte une size et une liste de widgets. La documentation confirme que la première page devient la page daccueil, que les pages apparaissent automatiquement dans la navigation dans lordre de déclaration, et quune page peut avoir jusqu’à 3 colonnes, avec 1 ou 2 colonnes full au minimum. citeturn16view0turn11view0

Autrement dit, dans le vocabulaire de Glance, ton futur “tab” nest pas une entité spéciale : cest simplement une nouvelle page Glance. Cest une bonne nouvelle, parce que ton onglet réseau peut donc être ajouté proprement comme page dédiée, avec une largeur wide et un ou plusieurs widgets de présentation. La doc donne dailleurs des largeurs default, slim et wide, et associe wide à 1920 px max, ce qui est pertinent pour ton souhait dafficher un très grand nombre de petites cases IP. citeturn11view0

Le système de widgets natifs est, lui aussi, très explicite. Dans internal/glance, on voit une série de fichiers widget-*.go et, côté templates, des fichiers *.html correspondants. La fonction newWidget dans widget.go enregistre les types connus comme calendar, clock, weather, monitor, extension, custom-api, iframe, html, server-stats, etc. Le même fichier définit une interface widget avec notamment Render(), initialize(), requiresUpdate(), update(), handleRequest() et les méthodes didentification/configuration. En pratique, cela veut dire quun widget natif Glance est un type Go compilé, pas un plugin chargé dynamiquement. citeturn3view0turn24view0

Le couplage avec le rendu HTML est fort, mais propre : les widgets vivent en Go, le rendu passe par des templates de html/template, et les assets/template sont embarqués dans le binaire. Jen déduis quajouter un widget natif implique toujours des modifications du code source Go, un template HTML et souvent une recompilation complète de lapplication. Ce nest pas une architecture à plugins dynamiques. citeturn24view0turn9view4turn4view1

Les options réalistes pour ajouter ton module

Officiellement, Glance documente quatre façons de créer des widgets personnalisés : iframe, html, extension et custom-api. Pour ton cas, les deux plus sérieuses sont custom-api, extension et, très franchement, iframe si tu veux une vraie mini-application interactive. Le mode html reste trop statique pour un module IPAM réseau un peu avancé. citeturn19view0turn12view0turn12view1turn12view4

Le widget custom-api est la voie la plus “Glance-compatible” quand on dispose déjà dune API JSON. La documentation officielle dit explicitement que sa configuration demande des notions de programmation, HTML, CSS, langage de templates Go et concepts propres à Glance. Elle précise aussi que le rendu repose sur html/template et gjson. Cest très puissant si ton backend de scan publie un JSON structuré et si tu acceptes un modèle daffichage plutôt déclaratif. Le dépôt glanceapp/community-widgets est dailleurs un excellent indicateur de la stratégie réelle du projet : il précise que la plupart des widgets communautaires sont faits avec custom-api, et que cette approche est beaucoup plus simple à déployer quune extension. citeturn12view0turn9view1turn18search2turn20search0

Le widget extension est plus souple côté langage, parce quil ne demande quun serveur HTTP renvoyant du contenu et quelques en-têtes spéciaux comme Widget-Title, Widget-Title-URL, Widget-Content-Type et Widget-Content-Frameless. Mais la doc officielle prévient de deux choses importantes : lAPI dextension est WIP et seul le type de contenu html est officiellement supporté pour linstant. Le dépôt communautaire confirme aussi que les extensions sont plus impliquées, car elles demandent un serveur ou conteneur séparé. Autrement dit : oui, cest faisable et élégant pour du Rust, mais cest moins stable et plus coûteux à maintenir quun custom-api. citeturn9view0turn12view1turn18search2

Le widget iframe est paradoxalement très intéressant pour toi. La doc officielle le décrit simplement comme un widget capable dembarquer une source externe avec une hauteur configurable. Ça paraît basique, mais pour un plan IP cliquable, avec volet de détails, rafraîchissement indépendant et éventuellement un peu de JS applicatif, cest en pratique lintégration la plus simple et la plus robuste : tu gardes Glance comme shell de navigation et tu exécutes ton mini-dashboard réseau comme une petite app web autonome. citeturn12view4turn19view0

Enfin, si tu veux modifier le cœur de Glance pour ajouter un widget “officiel” à ton fork, les technologies à employer sont sans ambiguïté : Go pour la logique, templates HTML Go pour le rendu, éventuellement un peu de JS/CSS statiques dans internal/glance/static, et la documentation YAML dans docs/configuration.md. Cest la voie la plus intégrée, mais aussi celle qui te lie le plus au codebase et aux conventions du projet. citeturn3view0turn13view1turn14view0turn24view0

Glance fournit néanmoins quelques points dextension utiles pour “hybrider” une approche custom : le serveur peut exposer un répertoire /assets/, le document global peut recevoir du HTML custom dans <head>, et le thème supporte un fichier CSS custom. En plus, chaque widget peut recevoir un css-class, et la doc explique que Glance utilise beaucoup de classes utilitaires, avec une classe widget-type-{name} pour cibler précisément un widget. En clair, tu peux très bien injecter ton propre CSS et même ton propre JS global si tu veux enrichir un custom-api avec un comportement plus dynamique. citeturn23view0turn23view2

Architecture recommandée pour ton onglet IP

Pour ton besoin précis, je recommande une page Glance dédiée nommée par exemple “Réseau”, en width: wide, avec une seule colonne full. La raison est simple : une page Glance ne peut avoir que trois colonnes maximum, alors que ton découpage logique contient déjà quatre sous-réseaux (10.0.0.x, 10.0.1.x, 10.0.2.x, 10.0.3.x), sans compter le volet de détail. En plus, un bloc /22 représente 1024 adresses IPv4 ; à cette échelle, il vaut mieux afficher un seul grand module full-width avec quatre sections internes, plutôt que disperser lUI dans les colonnes natives de Glance. citeturn11view0turn25calculator0

Je déconseille fortement dembarquer le moteur de ping directement dans Glance. Le produit nest pas conçu pour exécuter des sondes réseau en tâche de fond : il rafraîchit ses données au chargement de page, puis sappuie sur le cache de widget. Ton service de scan doit donc être un daemon séparé qui fait le travail lourd à la fréquence de ton choix et qui publie un état matérialisé que Glance ne fait quafficher. Cest le découpage le plus propre techniquement, mais aussi celui qui colle le mieux à larchitecture officielle du projet. citeturn19view0

Je te conseille aussi de ne pas assimiler “IP libre” à “ne répond pas au ping”. La documentation officielle de Nmap rappelle quun hôte actif peut être manqué si un firewall filtre les probes ou leurs réponses, et quun échec de découverte ICMP ne permet pas de conclure quune machine est réellement absente. Pour ton dashboard, il vaut mieux distinguer au moins alive, seen/used, reserved et free, où free ne devient vrai quen absence de réponse et en absence doccupation connue dans ton inventaire/DHCP/ARP. citeturn29search0turn29search1

Si tu veux le faire en Rust, le stack le plus cohérent pour le service externe est un backend de type Axum + Tokio, avec SQLite via SQLx si tu veux persister tes métadonnées riches (hardware, icon, services, link, location, parent, notes, dernières vues, etc.). Axum se présente comme une bibliothèque de routage HTTP et de handling ergonomique, Tokio comme le runtime asynchrone de référence pour I/O, timers et scheduling, et SQLx comme un toolkit SQL async “pure Rust” compatible SQLite. Pour ton besoin, cest un trio très logique. citeturn27search0turn27search5turn27search2

LAPI de ce service peut rester simple. Je te recommande un endpoint de synthèse pour le rendu global, et éventuellement un endpoint fin pour le détail dune IP. Par exemple :

{
  "last_scan_at": "2026-05-19T12:34:56Z",
  "sections": [
    {
      "name": "VM",
      "cidr": "10.0.0.0/24",
      "items": [
        {
          "ip": "10.0.0.12",
          "state": "alive",
          "hostname": "vm-proxy-01",
          "icon": "mdi:server",
          "link": "https://vm-proxy-01.local",
          "services": ["ssh", "https"],
          "location": "rack-a",
          "parent": "pve01"
        }
      ]
    }
  ]
}

Ce schéma nest pas imposé par Glance ; cest le bon niveau dabstraction pour séparer proprement scan, inventaire et présentation.

Pour lintégration visuelle dans Glance, tu as à mon avis deux architectures vraiment pertinentes. La première, que je considère comme la meilleure pour ton cas, est une page Glance + un widget iframe plein écran pointant vers une petite app web Rust. Tu gagnes tout de suite la liberté de faire une vraie grille, un volet latéral au clic, un polling indépendant, du tri, du filtrage, voire de la navigation clavier, sans dépendre du cycle de rendu de Glance. Cest la voie la plus simple si tu veux une UX riche. citeturn12view4turn19view0

La seconde est une approche plus native visuellement : un widget custom-api qui tire un JSON de ton service Rust, rend les cases via template HTML, puis sappuie sur css-class, custom-css-file, /assets/ et éventuellement un script injecté dans document.head pour animer un panneau de détails ou appliquer un peu de comportement côté client. Cette voie demande plus de bricolage frontend, mais elle garde un rendu plus “Glance”. Elle a aussi des précédents crédibles : le dépôt communautaire propose déjà des widgets Tailscale devices et NetAlertX Device Status qui affichent justement des équipements, leur statut et leurs IP à partir dune API externe. citeturn12view0turn23view0turn23view2turn21search0turn22search0turn22search2

Comme ton futur widget risque vite d’être long, ne mets pas tout en vrac dans glance.yml. La documentation Glance supporte les includes YAML avec $include, et le dépôt communautaire recommande justement de sortir les widgets complexes dans des fichiers dédiés. Pour ton cas, un fichier ipam.yml séparé est la bonne pratique. citeturn11view0turn20search0

Voici le squelette Glance que je recommanderais pour la solution la plus robuste, cest-à-dire une page dédiée + iframe :

pages:
  - name: Réseau
    width: wide
    columns:
      - size: full
        widgets:
          - type: iframe
            title: Plan IP
            source: http://ipam-ui:8088/
            height: 1200

Et si tu veux tenter le mode “plus natif Glance”, voici le principe hybride :

server:
  assets-path: /app/assets

document:
  head: |
    <script defer src="/assets/ipam.js"></script>

pages:
  - name: Réseau
    width: wide
    columns:
      - size: full
        widgets:
          - type: custom-api
            title: Plan IP
            cache: 15s
            css-class: ipam-grid
            url: http://ipam-api:8088/api/network/summary
            template: |
              <!-- grille + sous-sections + drawer -->

Le second modèle demande plus de finesse, mais il reste compatible avec les mécanismes officiellement documentés par Glance. citeturn12view0turn23view0turn23view2

Peut-on ajouter des widgets en Rust

Oui, mais pas de la même manière selon ce que tu appelles “ajouter un widget”. Si tu entends par là fournir les données et/ou le rendu depuis un service externe, alors oui, Rust convient très bien. Cest même une très bonne option, parce que Glance supporte officiellement un widget custom-api qui consomme du JSON, un widget extension basé sur un serveur HTTP externe, et un widget iframe qui peut embarquer nimporte quelle application web. Ces trois voies sont compatibles avec un backend écrit en Rust. citeturn12view0turn12view1turn12view4turn9view0

En revanche, si tu veux dire “ajouter un widget natif à Glance lui-même, dans le code de lapplication”, alors la réponse réaliste est non, pas en Rust dans le cadre normal du projet. Le code source montre que les widgets natifs sont des types Go enregistrés dans newWidget, que lapplication est construite comme un binaire Go unique, et que les templates/assets sont embarqués par go:embed. Le dépôt ne montre pas de système de plugins dynamiques ou de pont officiel vers Rust. Tu pourrais toujours bricoler une solution maison via FFI, sidecar ou fork très personnalisé, mais ce ne serait ni idiomatique pour Glance, ni dans lesprit “peu de dépendances / binaire simple” affiché par le projet. citeturn24view0turn4view1turn9view4turn19view0

Ma conclusion est donc nette : si ton vrai objectif est dobtenir rapidement un onglet IP réseau puissant et maintenable, fais-le en Rust à lextérieur de Glance, puis intègre-le dans Glance via iframe si tu veux la meilleure interactivité, ou via custom-api si tu veux un rendu plus natif et que tu acceptes davantage de contraintes côté UI. Si ton objectif est dajouter un widget “first-class” au codebase Glance, alors il faut le faire en Go, avec les templates et assets du projet. citeturn12view4turn12view0turn9view0turn24view0turn4view1