You pasted a hermes dump for help — and it carried your own API key

It’s 1am, your cron job failed again, so you do what you always do: run hermes dump, paste it into a Discord thread with a few log lines, and ask for help. Half an hour later someone DMs you — “hey, your OpenRouter key is still in the first block.”
That is not a hypothetical. hermes dump exists precisely to produce “a compact, plain-text summary of your setup that can be copy-pasted into Discord/GitHub” — and the part that was easiest to paste used to print each fallback provider’s API key verbatim. The fix (PR #127207, built on #126132 and #126168) landed on main on 2026-09-29 and turned redaction from one upload-time pass into two passes: one where the dump is generated, one where it leaves the machine.
First, know what you are actually sharing
Hermes has two sharing paths, and they blur together easily:
hermes dumpprints a configuration summary meant for copy-paste. By default it shows a key’s state as set/not set;--show-keysshows first and last four characters.hermes debug sharebundles logs (last 200 lines per log file by default), uploads them to a paste service, and prints a link.
The second command deserves a careful read:
--lines LINES Lines included per log file (default: 200)
--expire EXPIRE dpaste.com fallback retention in days (minimum 1; default 1)
--local Print the report locally instead of uploading
-y, --yes Skip confirmation and upload (required in scripts/CI)
--no-redact Disable upload-time secret redaction
--nous Upload to Nous-internal storage (AWS S3), not a public paste
Where the bundle lands changes the blast radius completely:
| Destination | Visibility | Retention | Deletable |
|---|---|---|---|
| paste.rs | public link | deleted after 6 hours | expires automatically |
| dpaste.com (fallback) | public link | number of days from --expire (default 1) |
cannot be deleted |
--nous (S3) |
Nous staff and allowlisted mods only, behind a Google-login viewer | 14 days | expires automatically |
So a public paste gives you a six-hour window, and if the upload falls back to dpaste the link sticks around for the retention you asked for — and you cannot remove it. Deciding this before you hit enter is worth far more than any cleanup afterwards.
What went wrong: str() on an object holding a key
The root cause is almost mundane. One block in the dump rendered the fallback provider list like any other object:
overrides["fallback_providers"] = str(fallbacks)
str() dutifully expanded each entry’s api_key field, plus any credentials embedded in its base_url (think https://user:token@gateway). Then came the second layer: _capture_dump() returned the bundle without any redaction at all — upload-time redaction only ran over the log snapshots, so the configuration section travelled naked.
You can check a bundle you already hold with one grep (just don’t run it in a public channel):
hermes dump | grep -n -i "api_key\|base_url\|token\|signature"
If you see a full api_key: sk-... value instead of ***, that bundle is not safe to publish.
After the fix: two forced passes and three shapes that used to slip through
The patch does three things, and each one explains why pasting is safe again.
One: the dump is redacted when it is built. Fallback provider entries now go through field-level masking first, then a forced text pass with strict URL-credential rules enabled:
overrides["fallback_providers"] = redact_sensitive_text(
str(_mask_fallback_keys(fallbacks)), force=True, redact_url_credentials=True
)
Two: the upload boundary redacts again on its own. _capture_dump() now force-redacts the dump and honours --no-redact — so even if a caller forgets, the bundle is checked once more before it leaves. Cloud signature keys are now in scope too: GCS X-Goog-Signature, Azure SAS sig, and X-Amz-Signature all count as URL credentials.
Three: field-level masking closes three blind spots in text redaction. This is the subtle one, and the most useful to understand. Text rules alone missed three api_key shapes:
- an unquoted numeric key (a YAML integer, not a string);
- a key written as a mapping instead of a scalar;
- a key that already contained
***, which the redactor treats as already masked.
Hence the ordering: mask by field name first, then turn the entries into text. Reverse the order and you still leak.
What to do on your own machine
Three habits that only became safe to rely on once this landed:
- Prefer
--nousover a public paste. When someone needs to read your logs,hermes debug share --nousputs them in storage that requires a login to view and deletes itself after 14 days. Fall back to a public paste only when the other person has no internal access, and accept the six-hour window. - Don’t reach for
--no-redactcasually. Its meaning is “do not redact before upload”, and the command prints a warning banner saying so. Use it only when you have reviewed the content line by line and genuinely need raw bytes (say, chasing a token prefix that masking hid). - Paste the dump into issues, never
config.yaml. The dump is designed to report set/not-set; the config file carries everything. It is the same rule we spelled out in the session export guide: redaction on an export or share path is the default, and the only thing you have to do is not switch it off.
Worth noting: --show-keys does not “show your keys” either — it shows first and last four characters so you can confirm which key is in use, not copy it.
Sanity-checking your own install
If you shared a bundle before late September, two minutes of checking is cheap:
hermes --version # v0.21.5 or later main already carries the fix
hermes dump | grep -c "api_key: \*\*\*" # expect greater than 0, i.e. field masking is live
If the first command reports an older build, update and check again. And if a public dpaste link is already out there, the only real remedy is rotating that key at the provider — which is exactly why looking before pasting beats any patch.
For the surrounding machinery — session mirrors, platform-chat and bot-chat egress rules — see the redaction lanes write-up; the full flag list lives on hermes dump and hermes debug.
The short version
- The fallback provider list in
hermes dumpused to printapi_keyvalues and credentials insidebase_url, because of astr()call plus an unredacted dump path. - There are now two forced passes: one when the dump is generated (field masking plus strict URL-credential rules), one at the upload boundary (
--no-redactaware). - The three shapes text redaction missed — numeric keys, mapping keys, keys containing
***— are covered by masking by field name first. - Prefer
--nous(private, 14 days) for shared logs; public pastes give you six hours, and the dpaste fallback cannot be deleted. --no-redactmeans “give up the protection”, not “more detail”. Paste the dump, not your config file.