NousResearch/hermes-agent · critical · RuntimeError
iron-proxy checksums.txt failed GPG signature verification —
Error message
iron-proxy checksums.txt failed GPG signature verification — refusing to install (possible release-channel tampering). gpg: {verify.stderr.decode('utf-8', 'replace')[:300]} What it means
The installer downloads checksums.txt.asc and the pinned public key, imports the key into an ephemeral GPG keyring, and verifies the detached signature. A present signature that FAILS verification is treated as a tamper signal and hard-fails the install (unlike a missing gpg/signature, which only warns and falls back to SHA-256). This is stricter than the checksum check alone because it catches a rewritten binary+checksum pair.
Source
Thrown at agent/proxy_sources/iron_proxy.py:623
imp = subprocess.run( # noqa: S603 — gpg path from trusted PATH lookup
[*base_cmd, "--import", str(pubkey_path)],
capture_output=True, timeout=60,
)
if imp.returncode != 0:
logger.warning(
"Could not import iron-proxy signing key — skipping GPG "
"verification (SHA-256 still enforced): %s",
imp.stderr.decode("utf-8", "replace")[:200],
)
return False
verify = subprocess.run( # noqa: S603
[*base_cmd, "--verify", str(sig_path), str(checksum_path)],
capture_output=True, timeout=60,
)
if verify.returncode != 0:
# A present signature that does NOT verify is a tamper signal — fail hard.
raise RuntimeError(
"iron-proxy checksums.txt failed GPG signature verification — "
"refusing to install (possible release-channel tampering). "
f"gpg: {verify.stderr.decode('utf-8', 'replace')[:300]}"
)
logger.info("Verified iron-proxy checksums.txt GPG signature.")
return True
def _expected_sha256(checksum_file: Path, asset_name: str) -> str:
"""Parse the standard ``sha256sum`` output: ``<hex> <filename>``."""
text = checksum_file.read_text(encoding="utf-8", errors="replace")
for line in text.splitlines():
parts = line.strip().split()
if len(parts) >= 2 and parts[-1] == asset_name:
return parts[0]
raise RuntimeError(
f"No checksum entry for {asset_name} in {checksum_file.name}"View on GitHub (pinned to c896c09c42)
Solutions
- Do not retry-bypass: verify the pinned key is still the project's current signing key (check the project's KEYLESS/changelog); if the key legitimately rotated, update the pinned key in iron_proxy.py.
- Re-download checksums.txt and checksums.txt.asc manually from the official release page and confirm `gpg --verify` against the published key — if it verifies there but not through Hermes, your download channel is being modified; fix the proxy/network.
- If the official release signature is genuinely broken, report it upstream and pin/rollback to the previous known-good _IRON_PROXY_VERSION.
Defensive patterns
Strategy: try-catch
Validate before calling
import shutil
def gpg_available() -> bool:
return shutil.which("gpg") is not None Try / catch
try:
find_iron_proxy(install_if_missing=True)
except RuntimeError as e:
if "GPG signature verification" in str(e):
# hard stop: never bypass; escalate for manual verification of the release channel
raise Prevention
- Keep the pinned signing key in iron_proxy.py in sync with upstream key rotations.
- Install from networks without content-rewriting middleboxes.
- Never catch-and-continue on this error; a verified-bad signature means the channel is untrusted.
When it happens
Trigger: find_iron_proxy(install_if_missing=True) / `hermes egress install` where gpg --verify of checksums.txt.asc exits non-zero: the served checksums.txt or .asc was modified in transit (MITM proxy), the release key rotated and the pinned key no longer matches, or the signature file itself is corrupted/truncated by the same flaky channel.
Common situations: Corporate TLS-intercepting proxies that also rewrite downloaded files; upstream re-signing releases with a new key after the pin in this file was set; a partially-downloaded .asc from a dropped connection.
Related errors
- Unsupported platform for iron-proxy auto-install: {system} {
- Checksum mismatch for {asset_name}: expected {expected}, got
- Failed to download {url}: {exc}
- No checksum entry for {asset_name} in {checksum_file.name}
- Could not find {binary_name} inside downloaded archive (memb
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/455c25bed48302b7.
Report an issue: GitHub.