Skip to content

[Feature Request] Suivi des ressources système de l'hôte et des process OpenFox #203

Description

@fullya99

English summary

OpenFox often runs in constrained or opaque environments like WSL2, a small VPS, a container, or a remote SSH box. In those setups there is no simple way to know if the host itself is healthy. Available memory, swap thrashing, disk space, CPU saturation, steal time, or the size of the OpenFox process tree are all invisible from inside the app. When a session slows down or dies, you cannot tell whether the model is struggling, the network is slow, or the host ran out of memory and the OOM killer stepped in.

This request proposes a passive telemetry layer. A static host inventory, sampled runtime metrics for the host and for the OpenFox process tree, and a few high level resource events. It stays separate from the execution engine, it is cgroup aware, local first, and read only in the MVP. This is a discussion, not a roadmap proposal. Feedback on scope, sampling strategy and the smallest useful MVP is welcome.

Problème observé

OpenFox est souvent installé ailleurs que sur un poste de dev confortable. Dans WSL2 sous Windows, sur un petit VPS, dans un conteneur, ou sur une machine distante en SSH. Dans ces contextes il n'y a aucun moyen simple de savoir :

  • combien de RAM il reste vraiment, et si la machine est en train de swapper
  • si le CPU est saturé, ou si le VPS subit du steal time
  • s'il reste de l'espace disque sur la partition du workspace, de la base et de /tmp
  • combien de process OpenFox tournent, lesquels, et ce qu'ils consomment
  • si un sous process lancé par une session (build, tests, navigateur headless, serveur MCP) est parti en vrille ou n'a jamais été nettoyé
  • si une session s'est arrêtée parce qu'elle a échoué, ou parce que l'OOM killer l'a tuée

Quand une session devient lente ou meurt, on ne sait pas à quoi l'attribuer. Du coup on relance, on tâtonne, on redémarre WSL au cas où. Installer Netdata ou Prometheus à côté est disproportionné pour ce besoin. Et surtout ces outils ne connaissent pas la notion de session OpenFox. Ils voient des PID, pas du travail.

Pourquoi ces environnements en particulier

Ce sont ceux où l'observation naïve donne des chiffres faux.

WSL2. /proc décrit la VM Linux, pas la machine Windows. La mémoire allouée dépend de .wslconfig et du ballooning, et côté Windows tout apparaît sous vmmem. On peut avoir 64 Go de RAM et une VM plafonnée à 8 Go sans le savoir.

Docker. /proc/meminfo et nproc reflètent l'hôte, pas les limites du conteneur. Les vraies limites sont dans les cgroups (memory.max, cpu.max).

VPS mutualisé. Le CPU annoncé n'est pas le CPU disponible. Sans le steal time on croit à une lenteur applicative alors que c'est l'hyperviseur qui préempte.

VPS sans swap. Pas de dégradation progressive, l'OOM killer tue directement. Sans trace, la session disparaît sans explication.

Trois choses différentes, à ne pas mélanger

Les specs statiques. Collectées une fois au démarrage, rafraîchies seulement si l'environnement change. Elles ne bougent pas, donc pas besoin de les échantillonner.

Les métriques de l'hôte. Échantillonnées à intervalle configurable, elles décrivent l'état courant de la machine.

Les métriques de l'arbre de process OpenFox. La part de cette consommation qui est réellement imputable à OpenFox et à ses enfants.

Sans la troisième, impossible de répondre à la seule question qui compte : est-ce OpenFox qui sature la machine, ou autre chose ?

Autre point important, toute valeur exposée doit être cgroup aware. Une métrique mémoire qui ignore les limites du conteneur, du cgroup ou de .wslconfig ne donne pas juste une information incomplète, elle donne une information fausse. Mieux vaut afficher clairement « limite inconnue » que la mémoire de l'hôte comme si elle était disponible.

Specs statiques proposées

  • OS, distribution, version du noyau, architecture
  • type d'environnement détecté (bare metal, VM, conteneur, WSL2, VPS)
  • CPU : modèle, cœurs physiques et logiques, fréquence de base
  • mémoire totale et swap total. L'absence de swap est une information en soi
  • disques : capacité et espace libre des points de montage critiques (workspace, base de données, /tmp)
  • limites effectives : cgroup v2 (memory.max, cpu.max, pids.max), configuration WSL, quotas éventuels, avec la source de la limite
  • versions des runtimes utilisés par OpenFox (Node, Python, gestionnaire de paquets, Docker si présent)

Métriques dynamiques proposées

Côté hôte :

  • charge moyenne (1, 5, 15) et utilisation CPU par cœur
  • steal time, indispensable en VPS, et iowait
  • mémoire utilisée, disponible, cache, et pression mémoire (PSI sous Linux quand c'est dispo)
  • swap : utilisation, et surtout taux d'entrée sortie. Le thrashing est un bien meilleur signal que le volume swappé
  • disque : espace libre sur les partitions critiques, débits lecture écriture
  • réseau : débit entrant et sortant, utile pour distinguer une lenteur locale d'une lenteur d'appel modèle
  • uptime, nombre total de process, throttling thermique si l'info est exposée

Côté arbre de process OpenFox :

  • l'arbre complet : serveur, workers, serveurs MCP, navigateurs headless, sous process lancés par les sessions (build, tests, git)
  • pour chaque process : PID, PPID, commande tronquée et expurgée, RSS, part CPU, threads, descripteurs de fichiers ouverts, durée de vie
  • les agrégats : mémoire résidente totale de l'arbre, part CPU totale, nombre de process
  • l'attribution à une session quand le lien de parenté le permet, sans l'inventer quand il est perdu

Contrat JSON versionné

Même logique que pour le watchdog. Un contrat explicite, consommable sans avoir à parser de la prose. Au minimum un schemaVersion, un horodatage de capture, un identifiant d'hôte stable, un bloc specs, un bloc host, un bloc processes, et les limites détectées avec leur source.

{
  "schemaVersion": "1.0",
  "capturedAt": "2026-08-02T14:31:07Z",
  "hostId": "stable-anonymous-id",
  "environment": { "kind": "wsl2", "detectedBy": "kernel-release" },
  "limits": {
    "memoryBytes": 8589934592,
    "source": "wslconfig",
    "cpuQuota": null
  },
  "host": {
    "cpu": { "loadAvg1": 3.2, "utilization": 0.87, "stealTime": 0.11 },
    "memory": { "usedBytes": 7300000000, "availableBytes": 1200000000, "pressure": "high" },
    "swap": { "usedBytes": 2100000000, "pagesInPerSec": 4200 },
    "disk": [{ "mount": "/home/user/openfox", "freeBytes": 3400000000, "freeRatio": 0.04 }]
  },
  "processes": {
    "totalRssBytes": 5100000000,
    "totalCpuShare": 0.62,
    "count": 14,
    "tree": []
  }
}

Les champs qu'on ne peut pas mesurer dans l'environnement courant doivent être explicitement null avec une raison. Jamais approximés.

Événements proposés

À exposer sur le même flux que les événements de session, pour qu'un seul consommateur suffise.

  • host.snapshot
  • host.memory.pressure
  • host.memory.critical
  • host.swap.thrashing
  • host.cpu.saturated
  • host.disk.low
  • host.disk.critical
  • process.spawned
  • process.exited
  • process.oom_killed
  • process.runaway (consommation anormale et prolongée)

process.oom_killed mérite une attention particulière. C'est l'événement qui explique après coup une session disparue sans la moindre erreur applicative.

Chaque événement doit être factuel, horodaté, et corrélé à un hostId, plus un sessionId quand c'est possible. Les événements de seuil ont besoin d'hystérésis, c'est à dire un seuil de déclenchement et un seuil de retour distincts, sinon ça clignote dès qu'on tourne autour de la valeur limite.

Échantillonnage et coût

Un moniteur qui pèse sur la machine qu'il surveille est contre productif. Donc :

  • intervalle d'échantillonnage configurable, avec une valeur par défaut prudente de l'ordre de quelques secondes
  • lecture directe de /proc et des cgroups plutôt que d'appeler des binaires externes en boucle
  • inventaire statique collecté une seule fois, pas à chaque cycle
  • rétention bornée et explicite (fenêtre glissante en mémoire, ou fichier plafonné)
  • possibilité de désactiver complètement la collecte

Dashboard en lecture seule

Idéalement sur la même surface que le dashboard de sessions, pour éviter d'avoir deux outils.

  • un bandeau hôte avec l'environnement détecté, les limites effectives, mémoire, swap, disque, CPU
  • l'arbre de process OpenFox avec la consommation de chaque nœud
  • la corrélation visuelle entre les alertes ressources et les sessions actives au même moment
  • des alertes sonores limitées aux seuils critiques uniquement (host.memory.critical, host.disk.critical, process.oom_killed)

MVP passif uniquement

  • collecte de l'inventaire statique
  • échantillonnage des métriques hôte et process
  • émission des événements de seuil
  • exposition en lecture seule (contrat JSON plus affichage)
  • aucune action corrective. Pas de kill, pas de throttling, pas de mise en pause de session, pas de nettoyage automatique de process

Évolutions possibles

  • corrélation explicite entre dégradation des ressources et ralentissement des sessions
  • backpressure, c'est à dire refuser de démarrer une nouvelle session quand la marge est insuffisante, sous contrôle explicite
  • historique persistant et export OpenMetrics pour ceux qui ont déjà une stack d'observabilité
  • métriques GPU pour les configurations en inférence locale

Hors périmètre

  • remplacer une solution d'APM ou de supervision généraliste comme Netdata, Prometheus ou Grafana
  • toute télémétrie sortante par défaut. La collecte doit rester locale, et tout envoi externe doit être opt in
  • exposer des chemins, variables d'environnement, secrets ou lignes de commande complètes sans expurgation
  • inventer des valeurs quand l'environnement ne permet pas de les mesurer
  • toute action corrective automatique sur les process

Sécurité et vie privée

Les métriques de process contiennent des lignes de commande, donc potentiellement des chemins, des noms de projets et des jetons passés en argument. Trois garde fous minimum. Expurgation des arguments par défaut. Aucune sortie réseau sans opt in explicite. Pas d'exigence de privilèges élevés, ce qui n'est lisible qu'en root doit dégrader proprement au lieu de réclamer des droits.

Questions aux mainteneurs

  • est ce que cette collecte a sa place dans le dépôt OpenFox, ou dans un consommateur externe branché sur le flux d'événements ?
  • faut il un module distinct, ou une extension du contrat d'état déjà envisagé pour les sessions ?
  • jusqu'où pousser l'attribution par session, sachant qu'un sous process orphelin perd son rattachement ?
  • quels seuils par défaut sont raisonnables, vu que ça dépend beaucoup de la taille de la machine ?
  • quel serait selon vous le plus petit MVP vraiment utile ? Est ce que l'inventaire statique seul lèverait déjà une bonne partie de la confusion ?

Références

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions