Tous les appels au modèle échouent après la v0.22.6 ? Vérifiez d'abord le chemin de SSL_CERT_FILE

Votre réseau d’entreprise passe par un proxy qui ouvre le HTTPS et l’inspecte. Vous avez réglé ça il y a des mois : export de la racine d’entreprise en bundle, export SSL_CERT_FILE=/etc/corp/ca-bundle.pem, et curl, pip et git se comportent normalement. Puis vous mettez Hermes à jour, envoyez un message, et vous le voyez se figer — certificate is not trusted dans le log, trois tentatives, et un sec « Provider temporarily unavailable ».
Votre réseau n’est pas cassé et vos réglages de proxy ne sont pas faux. Depuis la v0.22.6, le client de modèles résout la confiance des certificats par un nouveau chemin, et le bundle nommé dans SSL_CERT_FILE n’a pas suivi.
D’abord, confirmer qu’il s’agit bien de ce problème
Les symptômes sont assez caractéristiques :
- Tous les provider échouent, pas seulement un, et changer de modèle ne change rien.
- La chaîne d’erreur est
httpcore.ConnectError→httpx.ConnectError→openai.APIConnectionError: Connection error., avec"[host] certificate is not trusted"au milieu. curlouopenssl s_clientatteignent le même hôte sans problème, parce qu’ils lisentSSL_CERT_FILE.- Revenir à la v0.22.5 sur la même machine rétablit le fonctionnement normal.
Quatre sur quatre veut dire résolution de la confiance des certificats, pas politique réseau.
Ce qui a changé dans le code
Dans agent/ssl_verify.py, ajouté en v0.22.6, resolve_httpx_verify() répond à la question de la confiance contre laquelle ce client HTTP doit vérifier. L’ordre est : vérification explicitement désactivée → un bundle ssl_ca_cert explicite → le store de la plateforme. Le problème se situe juste avant cette dernière étape :
if os.environ.get("SSL_CERT_FILE") or os.environ.get("SSL_CERT_DIR"):
return _shared_context(None)
_shared_context(None) signifie « le store de la plateforme seul, sans CA bundle supplémentaire ». Exporter SSL_CERT_FILE vous pousse donc dans une branche qui l’ignore.
Le docstring du module énonce le contrat inverse : un bundle exporté dans SSL_CERT_FILE est approuvé en plus du store de la plateforme, et seule une configuration explicite par provider (ssl_ca_cert, ssl_verify: false) remplace cette confiance. platform_ssl_context(), dans le même fichier, implémente exactement cette union. La branche liée à la variable d’environnement et le reste du module ne disent pas la même chose.
Pourquoi Linux semblait aller bien est la partie intéressante : le backend OpenSSL de truststore appelle set_default_verify_paths(), et OpenSSL lit SSL_CERT_FILE de lui-même, donc la confiance arrivait par accident. macOS vérifie via SecTrustSetAnchorCertificates et Windows via CryptoAPI ; ni l’un ni l’autre ne consulte l’environnement d’OpenSSL, donc les deux échouent franchement. C’est pourquoi la même configuration d’entreprise casse le Mac d’un collègue pendant que votre machine Linux continue de fonctionner.
Trois façons de contourner, par ordre de préférence
1. Installer la racine dans le store de confiance du système (ce que recommandent les docs)
La documentation de sécurité est explicite : installez la racine d’un proxy TLS d’entreprise via la procédure de confiance du système d’exploitation en vigueur dans votre organisation. Une fois cela fait, SSL_CERT_FILE n’a plus d’importance pour les appels aux provider. Sous macOS, marquez le certificat comme toujours approuvé dans Trousseau d’accès ; sous Windows, importez-le dans les Autorités de certification racines de confiance via certlm.msc ; sous Debian/Ubuntu, déposez le pem dans /usr/local/share/ca-certificates/ et lancez update-ca-certificates. Option la plus propre, survit aux mises à jour, exige des droits admin.
2. Pointer le provider explicitement vers le bundle
Si vous ne pouvez pas toucher au store système, définissez le CA bundle sur l’entrée du provider :
providers:
deepseek:
api: https://api.deepseek.com
key_env: DEEPSEEK_API_KEY
ssl_ca_cert: ${env:SSL_CERT_FILE}
ssl_ca_cert remplace la confiance de la plateforme pour le chat, les métadonnées de modèle et les sondes de catalogue de cet endpoint. Cela ne répare que ce provider, ce qui est généralement ce que vous voulez ; un chemin erroné journalise un avertissement et retombe sur la confiance de la plateforme au lieu d’échouer en silence.
3. Désactiver la vérification (développement local uniquement)
ssl_verify: false fait marcher les appels immédiatement et vous expose à tous les man-in-the-middle du chemin — derrière un proxy d’entreprise qui inspecte, cela revient à faire confiance au proxy sans réserve. La documentation le juge dangereux sur des réseaux non fiables et déconseille d’en faire un correctif permanent à une racine manquante. À utiliser pour diagnostiquer, pas pour livrer.
Comment cela sera corrigé
La PR de correctif (#148253) fait renvoyer platform_ssl_context() à cette branche au lieu de _shared_context(None), rétablit l’union documentée et explique pourquoi Linux semblait fonctionner. À la date de la v0.22.7, elle est encore ouverte : la build stable que vous installez aujourd’hui se comporte donc comme avant. Si vous l’attendez, la vérification est simple : une version plus tard, regardez si cette branche de agent/ssl_verify.py renvoie platform_ssl_context().
Une habitude à garder
Les problèmes de certificats se déguisent en « le réseau est tombé » ou « le provider a une panne », parce que le symptôme visible est une erreur de connexion. La prochaine fois que tous les provider échouent d’un coup, faites d’abord un test par couches : curl --cacert "$SSL_CERT_FILE" https://<host>. Si ça passe, la panne est dans la résolution de confiance de l’application, pas dans le réseau — et vous venez d’économiser une demi-heure de captures de paquets.
Pour aller plus loin : modèles locaux et certificats auto-signés, chiffrer les secrets en local, ce qu’il y a d’autre dans la fenêtre v0.22.7, installer et mettre à jour.