Le coupable de l'OOM de 3 h du matin : les processus orphelins laissés par votre agent

Le ventilateur du portable se remet à tourner, localhost:3000 annonce que le port est pris, et ps aux affiche quatre processus node et python que vous ne reconnaissez pas. Quelque part au fond de votre mémoire : la semaine dernière, vous avez demandé à l’agent de « lancer un serveur et tester », la session s’est terminée, le serveur non. Ce que ces rescapés ont en commun, c’est que personne ne s’en souvient et que rien ne tient les comptes — ce qui est précisément le métier de agent-janitor.
D’où viennent les processus orphelins
Un appel de tool d’agent ressemble beaucoup à vous tapant des commandes dans un terminal. Vous lui demandez de lancer npm run dev en arrière-plan, d’ouvrir un tunnel cloudflared ou d’attacher un watcher de fichiers ; la commande rend la main, le processus continue. Quand la session se termine et que son terminal disparaît, Linux réattribue le survivant au PID 1 — c’est ce que signifie ppid == 1, et c’est la signature d’« orphelin » la plus fiable qui existe.
Le coût est concret : des ports pris, de la RAM engloutie, un OOM à 3 h du matin, et la fois suivante l’agent ne peut plus joindre son propre service. Pire encore, le coût de diagnostic — vous n’avez aucune idée de qui a lancé ce python app.py.
Il juge sur preuves, pas à l’intuition
agent-janitor note les processus au lieu de tirer sur tout ce qui paraît suspect :
| Preuve | Confiance |
|---|---|
| Réattribué à init (ppid=1) et détient un port / traits de serveur de dev / TTL dépassé | confirmed |
| Réattribué à init (ppid=1) seulement | likely |
| Traits de dev et TTL dépassé, mais le parent est toujours vivant | possible (jamais balayé automatiquement) |
Tout ce qu’il lit est local : ppid et lignes de commande depuis /proc, sockets en écoute depuis /proc/net/tcp{,6}, et un seuil d’âge. Seuls confirmed et likely sont tuables.
Deux classes sont exemptées par leur nom : les démons système (multipathd, udisksd, ModemManager, systemd-* et compagnie), et les agents de gestion d’hôte ou de VM (tailscaled, agents de fournisseurs cloud, amazon-ssm-agent, walinuxagent, google_guest_agent). Vous ajoutez votre propre infrastructure à une liste blanche :
janitor whitelist add ngrok
janitor whitelist add cloudflared
janitor whitelist add tmux
Les entrées de la liste blanche sont des sous-chaînes comparées à la ligne de commande (sensibles à la casse, échappées en regex, sans injection). Un tableau vide ou un fichier absent signifie que rien n’est en liste blanche.
Installer, puis scanner avant de balayer
Il vit dans le catalogue curaté (agent-janitor 0.3.1, niveau communautaire, nécessite Hermes >= 0.21.6). Installez par son nom si votre catalogue le liste, sinon depuis le dépôt comme le montre le README :
git clone https://github.com/ZidoCode/agent-janitor ~/.hermes/plugins/agent-janitor
hermes plugins enable agent-janitor
Maintenant, contentez-vous de scanner — ne nettoyez encore rien :
python3 ~/.hermes/plugins/agent-janitor/janitor_cli.py scan
python3 ~/.hermes/plugins/agent-janitor/janitor_cli.py sweep --ttl-h 6
Cette seconde commande a l’air inquiétante, mais c’est une répétition à blanc par défaut : les décisions sont affichées et journalisées en would_kill, rien ne meurt.
Un seul fichier débloque le vrai kill — <hermes-home>/plugin-data/agent-janitor/policy_mode, dont le contenu doit être exactement enforce :
echo enforce > ~/.hermes/plugin-data/agent-janitor/policy_mode
Ce choix de conception mérite d’être copié : sûr par défaut, et l’état risqué exige une décision écrite.
Avant d’activer enforce, faites ces cinq choses
- Remplissez d’abord la liste blanche. Votre tunnel, votre reverse proxy, tmux, tout ce qui est « lancé par l’agent mais gardé exprès » y entre avec
janitor whitelist add. - Lisez la sortie de la répétition à blanc comme la liste des kills. C’est exactement ce sur quoi enforce agira, ni plus ni moins.
- Sachez qui est visé. La règle est « réattribué au PID 1 depuis plus d’une minute, non exempté, pas en liste blanche » — il ne vérifie pas qui a lancé le processus. Un
hermes gatewaylancé avecnohupou un serveur tmux est une cible légitime en mode enforce si vous ne l’avez pas mis en liste blanche, et exécuté en root, cela inclut les processus des autres utilisateurs. - Sachez ce qu’il laisse derrière.
spawns.jsonlenregistre les commandes de terminal qui ressemblaient à un lancement de travail en arrière-plan, etsweeps.jsonlenregistre chaque décision (would_killou tué), de quoi auditer le balayage après coup. - Sachez l’ordre du kill. SIGTERM, deux secondes d’attente, SIGKILL — et avant le
kill -9il relit la ligne de commande, en abandonnant si le PID a été recyclé. Pas de fratricide par réutilisation de PID.
Associez-le au registre pour l’attribution
Seul, janitor ne peut dire que « ce processus est un orphelin ». Avec agent-ledger du même auteur installé, il lit le registre et transforme un python app.py anonyme en « lancé par cet appel de tool précis sur le port 8080, il y a 12 jours ». L’attribution reste approximative — elle se base sur le nom de base de l’exécutable — donc prenez-la pour un indice, pas pour une preuve. L’auteur la présente de la même façon.
Trois limites à connaître absolument
- Linux uniquement, parce qu’il lit
/proc. macOS et Windows ne sont pas pris en charge : sur un Mac, cet outil appartient à vos serveurs, pas à votre portable. - La mise en veille gonfle l’uptime. L’uptime est relatif au démarrage et continue de compter pendant que la machine dort, donc des faux positifs de TTL sont possibles sur les machines dont on rabat le capot. Sur un serveur — la cible principale — cela ne s’applique pas.
- La liste blanche compare des sous-chaînes de ligne de commande : un shell mis en liste blanche qui lance ensuite quelque chose de dangereux lui est donc invisible.
Pour réduire en général le problème du « je ne sais pas ce que mon agent a fait » : janitor couvre la dimension processus, l’empreinte des skills couvre la dimension skill, et cron doctor couvre les tâches planifiées. Pour la routine générale d’installation et d’épinglage, voyez le guide du catalogue de plugins.