Un navigateur cloud bridé ne tue plus browser_exec


Vous avez configuré un navigateur cloud pour que Hermes capture chaque matin une capture d’écran de votre tableau de bord et la dépose dans un canal. Tout a fonctionné pendant une semaine, puis un matin, à la place du graphique, vous recevez une erreur : returned no CDP endpoint, so Browser Use mode cannot drive it. La page va bien. Votre script va bien. Le service de navigateur cloud vous limitait un instant (rate limiting) — et toute la tâche du navigateur est morte avec lui.

Le plus troublant : les outils browser_* intégrés de Hermes ont continué de fonctionner dans exactement les mêmes conditions. Seul browser_exec est tombé.

Deux façons dont le navigateur se bloquait

Les deux symptômes viennent de la même chose. Une fois un navigateur cloud configuré (browser.cloud_provider, choisi parmi browserbase, browser-use, camofox, nous ou local), il devrait rester un chemin local disponible sur la machine — mais browser_exec n’y accédait pas.

Un : la création de session échoue, l’appel échoue

Quand le fournisseur cloud échouait dès la création de session, browser_exec échouait purement et simplement :

Cloud browser provider BrowserUseBrowserProvider returned no CDP endpoint,
so Browser Use mode cannot drive it. Switch to the built-in browser tools
for this provider.

La panne observée en vrai était un rate limit : la passerelle a répondu 429 RATE_LIMIT_EXCEEDED avec retryAfter 17 et limit 200.

La cause racine est que browser_exec et les outils intégrés ne suivaient pas le même chemin. Les outils de navigateur intégrés basculent déjà sur le Chromium local embarqué quand aucune session locale ne peut être créée, en marquant la session fallback_from_cloud. browser_exec ne lisait que cdp_url dans cette session — or une session locale n’a pas de cdp_url tant que son Chromium n’a pas démarré. Il levait donc l’erreur ci-dessus et en accusait le fournisseur.

Un amplificateur s’ajoute à cela : la session en échec reste en cache. Les appels suivants avec la même session key retombaient sur la même session cassée jusqu’à ce que le reaper d’inactivité la supprime. Un seul 429 pouvait ruiner les minutes suivantes de travail avec le navigateur.

Le correctif est volontairement minime. Dans tools/browser_use_cli.py::_resolve_backend_cdp, lorsque la session du fournisseur revient avec fallback_from_cloud, on résout l’endpoint CDP du Chromium embarqué comme le fait le chemin sans fournisseur, et on journalise l’échec du fournisseur une seule fois. Un fournisseur qui renvoie une session sans CDP URL et sans fallback échoue toujours avec le même message — rien ne fait semblant de fonctionner.

Deux : le navigateur cloud n’atteint pas votre localhost

Le second symptôme est plus courant : vous avez un serveur de dev sur cette machine, vous demandez http://localhost:3000, et vous obtenez :

Blocked: URL targets a private or internal address

browser_exec pré-analyse les URL de votre code par sécurité, mais il appelait ce contrôle sans le verdict du routage hybride. Avec cloud_provider défini, le backend compte comme non local, donc chaque adresse privée était rejetée.

browser_navigate n’a jamais eu ce problème — il route les URL privées vers un sidecar local. Deux chemins dans un même sous-système de navigateur, deux comportements différents ; ce changement les aligne.

Quelle URL va où

Adresse que vous visez Qui l’ouvre
https://example.com le navigateur cloud configuré
http://localhost:3000 sidecar Chromium local sous la clé de cache ::local
http://192.168.1.9 (LAN) sidecar Chromium local
169.254.169.254 (métadonnées cloud) bloqué sans condition, sans backend
un appel qui mélange URL publiques et privées tout l’appel s’exécute en local

Quelques détails d’implémentation comptent, car ils sont porteurs pour la sécurité :

  • Le prédicat de routage _hybrid_routes_locally(url) est désormais partagé par browser_navigate et browser_exec, donc les deux ne peuvent plus diverger ;
  • Chaque URL du code est évaluée une seule fois, et ce verdict unique décide à la fois du contrôle de sécurité et de la route. Un hôte qui résout en privé puis se réattribue une IP publique ne peut pas relâcher le contrôle des adresses privées tout en étant quand même envoyé vers le cloud ;
  • Un appel qui mélange URL publiques et privées s’exécute entièrement en local — la partie privée ne fuit jamais vers le haut ;
  • Le sidecar utilise la clé de cache ::local et tourne sous son propre harness daemon (BU_NAME=_local_<name>). Le daemon lit son environnement CDP une fois au démarrage, donc partager le daemon de la route cloud relaierait l’URL privée vers le navigateur cloud. Des daemons séparés, pas un daemon partagé ;
  • Un appel suivant sans littéral d’URL reste sur la route empruntée par l’appel précédent.

Le plancher de sécurité n’a pas bougé

Les fallbacks sont un endroit facile pour introduire une faille, donc tous les invariants existants ont été conservés :

  • Les adresses de métadonnées cloud restent bloquées sans condition : 169.254.169.254 répond toujours « URL targets a cloud metadata endpoint », quel que soit le fournisseur ;
  • browser.auto_local_for_private_urls est activé par défaut ; le mettre à false rejette toujours les URL privées, sauf si vous définissez aussi browser.allow_private_urls, ce qui laisse le fournisseur cloud tenter sa chance — souvent peine perdue, car Browserbase et consorts n’atteignent pas votre LAN ;
  • Un override CDP n’est pas considéré comme local : si vous définissez browser.cdp_url ou utilisez /browser connect, le routage hybride renvoie faux et le comportement est inchangé ;
  • Une redirection de public vers privé est refusée, pas laissée passer ;
  • La revérification de l’URL finale après exécution n’est pas touchée.

Le fallback local a besoin du sidecar local — la même CLI agent-browser qu’utilise le mode purement local. hermes setup tools → Browser Automation l’installe pour vous.

État et mise à jour

Les deux correctifs (PRs #136447 et #136442) ont été fusionnés dans main le 2026-10-11. Le tag actuel, v0.21.6 du 8 octobre, ne les inclut pas ; la prochaine release le fera. Pour les utiliser dès aujourd’hui, mettez à jour depuis main.

Après la mise à jour, vous pouvez confirmer que le fallback s’est enclenché en cherchant cette unique ligne d’échec du fournisseur dans le log — elle apparaît une fois, puis l’appel du navigateur continue sur le Chromium local.

Pour conclure

Un navigateur cloud est rapide et pratique, mais c’est un service externe : il applique des rate limits et il tombe. La configuration qui tient vraiment, c’est cloud d’abord, local comme plancher, et des adresses sensibles qui ne quittent jamais la machine. C’est ce plancher que ces changements rétablissent : une panne de fournisseur ne met plus fin à votre tâche, les serveurs de dev et les pages LAN s’ouvrent comme avant, et ni les adresses internes ni les endpoints de métadonnées cloud ne sont passés près du cloud.

À lire aussi : comment les outils de navigateur intégrés sont devenus par défaut explique le choix du fournisseur, le budget de snapshots du navigateur traite un autre type de « le navigateur est lent », et les défis du navigateur en offre gratuite couvre les compromis d’un navigateur cloud à petit budget.