NousResearch/hermes-agent · error · RuntimeError

openssl not found on PATH. Install OpenSSL (apt: `openssl`,

Error message

openssl not found on PATH. Install OpenSSL (apt: `openssl`, brew: `openssl`) to generate the iron-proxy CA cert.

What it means

ensure_ca_cert() generates the local CA (used by iron-proxy to mint short-lived leaf certs for TLS interception) by shelling out to the `openssl` CLI, avoiding a cryptography-package dependency. If shutil.which('openssl') is None it refuses with an actionable install hint. Existing ca.crt/ca.key short-circuit this, so it only fires when the CA must actually be (re)generated.

Source

Thrown at agent/proxy_sources/iron_proxy.py:747

def ensure_ca_cert(*, force: bool = False) -> Tuple[Path, Path]:
    """Generate (or return existing) iron-proxy CA cert + key.

    Uses the host's ``openssl`` binary.  We don't try to bind to a Python
    crypto library — openssl is universally available on the platforms we
    support, and it sidesteps cryptography-package licensing/distribution
    surface.
    """

    state = _proxy_state_dir()
    ca_crt = state / "ca.crt"
    ca_key = state / "ca.key"

    if ca_crt.exists() and ca_key.exists() and not force:
        return ca_crt, ca_key

    if shutil.which("openssl") is None:
        raise RuntimeError(
            "openssl not found on PATH. Install OpenSSL (apt: `openssl`, "
            "brew: `openssl`) to generate the iron-proxy CA cert."
        )

    # 10-year cert.  iron-proxy mints short-lived leaf certs from this CA,
    # so the CA itself only rotates when the user explicitly forces it.
    with tempfile.TemporaryDirectory(prefix="hermes-proxy-ca-") as tmpdir:
        tmp = Path(tmpdir)
        tmp_key = tmp / "ca.key"
        tmp_crt = tmp / "ca.crt"

        subprocess.run(  # noqa: S603 — openssl path is trusted PATH lookup
            ["openssl", "genrsa", "-out", str(tmp_key), "4096"],
            check=True,
            capture_output=True,
            timeout=60,
        )
        subprocess.run(  # noqa: S603

View on GitHub (pinned to c896c09c42)

Solutions

  1. Install OpenSSL: `apt-get install -y openssl` (Debian/Ubuntu), `apk add openssl` (Alpine), or `brew install openssl` (macOS) and ensure it is on PATH.
  2. If you already have a CA, place ca.crt and ca.key in the proxy state dir so ensure_ca_cert() reuses them instead of generating.
  3. For containers, add openssl to the image rather than installing at runtime.

Example fix

// before
ensure_ca_cert(force=True)  # RuntimeError: openssl not found on PATH

// after
import shutil
if shutil.which("openssl") is None:
    raise SystemExit("install openssl first: apt-get install -y openssl")
ensure_ca_cert(force=True)
Defensive patterns

Strategy: validation

Validate before calling

import shutil

def can_generate_ca() -> bool:
    return shutil.which("openssl") is not None

Try / catch

try:
    ensure_ca_cert()
except RuntimeError as e:
    if "openssl not found" in str(e):
        raise SystemExit("Install openssl (apt: openssl, brew: openssl) before egress setup")

Prevention

When it happens

Trigger: Calling ensure_ca_cert() or ensure_ca_cert(force=True) (or `hermes egress setup`) on a host without the openssl binary on PATH — minimal containers (slim Docker images), hardened base images, or systems where openssl is installed but not on PATH.

Common situations: python:*-slim Docker images and distroless-adjacent containers; macOS after removing/renaming Homebrew openssl; first-ever `hermes egress setup` on a stripped-down server.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/a917e897b1dd6609. Report an issue: GitHub.