fix(agent,gateway,doctor): add SSL CA cert bundle fail-fast guard

A stale certifi CA bundle after a partial `hermes update` used to crash
the agent on the first outbound HTTPS call with a raw traceback and
trap the gateway in a retry loop.

This patch:

* Adds `agent/errors.py` with a typed `SSLConfigurationError`
* Adds `agent/ssl_guard.py` with a `verify_ca_bundle()` pre-flight
  that asserts the bundle exists, is non-trivial in size, and can build
  a working SSLContext. On macOS, it falls back to the system trust
  store when the bundle is empty but the system store is healthy
  (covers corporate proxies / MDM setups).
* Wires the guard into `run_agent.py` and `gateway/run.py` right
  after the `hermes_bootstrap` import, inside a try/except so a bug
  in the guard itself can never prevent startup.
* Adds a `SSL / CA Certificates` section to `hermes_cli doctor` so
  users can detect the failure with one command.
* Adds unit tests covering the healthy, missing, empty, skip-env, and
  macOS-fallback paths.
* Adds an RCA document describing the failure mode and the recovery
  path (`pip install -e .`).

When the bundle is broken the user sees:

    \u26a0\ufe0f SSL certificate bundle issue detected.
       Run: pip install -e .

`HERMES_SKIP_SSL_GUARD=1` disables the check for sandboxed
environments that ship their own trust store.
This commit is contained in:
chromalinx
2026-06-13 21:14:32 -07:00
committed by Teknium
parent 1106879147
commit a218a0f156
7 changed files with 244 additions and 3 deletions
+21 -1
View File
@@ -306,6 +306,23 @@ def _check_s6_supervision(issues: list[str]) -> None:
)
def check_certificates() -> None:
"""Verify the certifi CA bundle is loadable.
Surfaces the SSLConfigurationError user-friendly path before they hit
a wall of tracebacks on the first outbound HTTPS call.
"""
try:
from agent.ssl_guard import verify_ca_bundle_with_fallback
from agent.errors import SSLConfigurationError
verify_ca_bundle_with_fallback()
check_ok("SSL CA certificate bundle is valid")
except SSLConfigurationError as e:
check_fail("SSL CA certificate bundle is broken", str(e))
except Exception as e:
check_warn("SSL certificate check skipped", str(e))
def _check_gateway_service_linger(issues: list[str]) -> None:
"""Warn when a systemd user gateway service will stop after logout.
@@ -567,7 +584,10 @@ def run_doctor(args):
# Detect drift between pyproject.toml and hermes_cli/__init__.py versions
# (a git conflict resolution can silently revert one but not the other).
_check_version_consistency(issues)
_section("SSL / CA Certificates")
check_certificates()
_section("Required Packages")
required_packages = [
("openai", "OpenAI SDK"),