Problem or Use Case
Summary
When a chat request fails (401 / 429 / 500 / etc.), Hermes dumps the entire request—including the Authorization: Bearer sk-... header—to a JSON file at ~/.hermes/sessions/request_dump_<session_id>_<timestamp>.json in plaintext. The project ships agent/redact.py with a redact_sensitive_text function used in 11+ other places, but the dump path bypasses redaction.
This is unfortunate placement for a security-sensitive piece of data:
- Dumps are exactly what users
tar -czf bug-report.tar.gz ~/.hermes/ and email/upload when reporting bugs
- A single failed session in our environment produced 5 dump files, each containing the full plaintext key
grep -r 'sk-proj-' ~/.hermes/ continues to find the old key even after rotating via hermes setup
Evidence
redact_sensitive_text IS used in tool output paths (11 call sites)
tools/terminal_tool.py:2084 terminal output
tools/code_execution_tool.py:895/896 code exec stdout
tools/code_execution_tool.py:1240/1241/1242 code exec stdout/stderr
tools/file_tools.py:553/573/996 file reads
tools/web_tools.py:1241 web tools (uses _PREFIX_RE constant)
redact_sensitive_text IS NOT used in the dump path
# run_agent.py:4247 (origin/main HEAD 6f2dab248; same logic at line 4215 in v2026.4.30-128-g0159f25fd)
dump_file = self.logs_dir / f"request_dump_{self.session_id}_{timestamp}.json"
dump_file.write_text(
json.dumps(dump_payload, ensure_ascii=False, indent=2, default=str),
encoding="utf-8",
)
dump_payload is serialized straight to disk via json.dumps with no transformation. Grepping the surrounding 8 lines for redact returns nothing.
Notable: line 4255 introduces an HERMES_DUMP_REQUEST_STDOUT toggle (added in recent upstream commits), so the dump-writing code has been actively touched on main—but redact still wasn't wired up in the same touch. This makes the omission look like an oversight rather than an intentional design choice.
Asymmetry
The architecture is unintuitively backwards: model tool stdout (which is unlikely to contain raw secrets) goes through redact, but failed-request dumps (which always contain Authorization headers and which users actually share with bug reports) do not.
cli.py:593-595 reveals an HERMES_REDACT_SECRETS toggle exists, suggesting the author was aware that redaction is configurable—but even with the toggle ON (default), the dump path still writes plaintext keys (verified empirically: 5 dump files in our deployment, all with Authorization: Bearer sk-proj-...XXXX visible).
Reproduction
- Configure any provider with a valid key
- Make the chat sub-process produce a failure (e.g. revoke the key mid-session, or temporarily change it to something invalid)
- Send a chat message
- Inspect
~/.hermes/sessions/request_dump_<session>_<timestamp>.json
- Search for
Bearer sk- — full key is present in plaintext
Suggested Fixes (in order of preference)
- Pipe the dump body through
redact_sensitive_text before writing. Consistent with how tool output is handled.
- Strip
Authorization (and X-Api-Key, Cookie, etc.) from headers before writing. Even if the body is preserved verbatim, headers are easier to enumerate.
- Add a
HERMES_DUMP_FAILED_REQUESTS toggle defaulting to OFF. Dumps are a power-user debugging feature; default-on is wrong for a tool that handles credentials.
Workaround for operators (current state)
sudo rm -f /srv/hermes-agent/home/.hermes/sessions/request_dump_*.json
A nightly cron job is recommended for any deployment where users might package up ~/.hermes/ for support.
Environment
- Hermes Agent v0.12.0 (2026.4.30, deployed at commit
v2026.4.30-128-g0159f25fd)
- Bug also confirmed present on
origin/main HEAD 6f2dab248 as of 2026-05-03 (62 commits ahead of deployed version; no relevant fix in those commits — agent/redact.py is 0 changes, run_agent.py was modified but redact still not wired up at the dump path)
- Python 3.11.15
- OpenAI SDK 2.32.0
Bug analysis, evidence collection, and issue write-up by Claude (Anthropic). Reproduction performed on a production deployment that produced 5 plaintext-key dump files during a single debugging session.
---
### Proposed Solution
-
### Alternatives Considered
_No response_
### Feature Type
New tool
### Scope
None
### Contribution
- [ ] I'd like to implement this myself and submit a PR
### Debug Report (optional)
```shell
Problem or Use Case
Summary
When a chat request fails (401 / 429 / 500 / etc.), Hermes dumps the entire request—including the
Authorization: Bearer sk-...header—to a JSON file at~/.hermes/sessions/request_dump_<session_id>_<timestamp>.jsonin plaintext. The project shipsagent/redact.pywith aredact_sensitive_textfunction used in 11+ other places, but the dump path bypasses redaction.This is unfortunate placement for a security-sensitive piece of data:
tar -czf bug-report.tar.gz ~/.hermes/and email/upload when reporting bugsgrep -r 'sk-proj-' ~/.hermes/continues to find the old key even after rotating viahermes setupEvidence
redact_sensitive_text IS used in tool output paths (11 call sites)
redact_sensitive_text IS NOT used in the dump path
dump_payloadis serialized straight to disk viajson.dumpswith no transformation. Grepping the surrounding 8 lines forredactreturns nothing.Notable: line 4255 introduces an
HERMES_DUMP_REQUEST_STDOUTtoggle (added in recent upstream commits), so the dump-writing code has been actively touched onmain—but redact still wasn't wired up in the same touch. This makes the omission look like an oversight rather than an intentional design choice.Asymmetry
The architecture is unintuitively backwards: model tool stdout (which is unlikely to contain raw secrets) goes through redact, but failed-request dumps (which always contain Authorization headers and which users actually share with bug reports) do not.
cli.py:593-595reveals anHERMES_REDACT_SECRETStoggle exists, suggesting the author was aware that redaction is configurable—but even with the toggle ON (default), the dump path still writes plaintext keys (verified empirically: 5 dump files in our deployment, all withAuthorization: Bearer sk-proj-...XXXXvisible).Reproduction
~/.hermes/sessions/request_dump_<session>_<timestamp>.jsonBearer sk-— full key is present in plaintextSuggested Fixes (in order of preference)
redact_sensitive_textbefore writing. Consistent with how tool output is handled.Authorization(andX-Api-Key,Cookie, etc.) from headers before writing. Even if the body is preserved verbatim, headers are easier to enumerate.HERMES_DUMP_FAILED_REQUESTStoggle defaulting to OFF. Dumps are a power-user debugging feature; default-on is wrong for a tool that handles credentials.Workaround for operators (current state)
sudo rm -f /srv/hermes-agent/home/.hermes/sessions/request_dump_*.jsonA nightly cron job is recommended for any deployment where users might package up
~/.hermes/for support.Environment
v2026.4.30-128-g0159f25fd)origin/mainHEAD6f2dab248as of 2026-05-03 (62 commits ahead of deployed version; no relevant fix in those commits —agent/redact.pyis 0 changes,run_agent.pywas modified but redact still not wired up at the dump path)Bug analysis, evidence collection, and issue write-up by Claude (Anthropic). Reproduction performed on a production deployment that produced 5 plaintext-key dump files during a single debugging session.