一次智能體工作階段後,API 金鑰可能留在 8 個位置:如何逐一檢查
After One Agent Session, Your API Key Can Sit in 8 Places. How to Check Each
文章整理一次智能體工作階段後,API 金鑰可能殘留的 8 個位置,並提供逐項搜尋檢查的方法。清單涵蓋 Shell 歷史記錄、智能體逐字稿、終端機捲動記錄、程式碼庫中的環境檔案與測試資料、MCP 設定、CI 日誌、PR 截圖,以及智能體記憶或規則檔案。作者建議先撤銷舊金鑰並簽發新金鑰,再搜尋及清理副本;Hermes IDE 不會修改 Shell 自身的歷史記錄。
- Book: RAG Pocket Guide
- The series: Pocket Guides for Developers — 8 short books on agents, RAG, prompts, observability, events, databases and system design — the whole series
- My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools
- Me: xgabriel.com | GitHub
You asked the agent why the request kept returning 401. You exported the key in your shell and pasted it into the chat so it could test the call, and the test passed. Then you closed the session, and the key is still on disk in places you never looked.
Claude Code's own docs are blunt about part of this: its transcripts are plaintext, and "anything that passes through a tool is written to a transcript on disk: file contents, command output, pasted text" (Claude Code docs). That is one copy, and a normal session can make seven more.
Do one thing before you search: revoke the old key and issue a new one. GitHub's guide on leaked secrets puts revoking or rotating first, before any history rewrite, because a key that no longer works can't be used from wherever it sits (GitHub Docs). The checklist below finds the copies, so you know what was exposed and the next key doesn't follow the same trail.
Every search needs a piece of the old key. Don't type it as an argument, or you've just added copy number nine to your shell history. Read it silently into a variable instead. read -s doesn't echo in bash or zsh:
# paste the last 12 characters of the old key, press Enter
read -rs K
# ... run the checks below ...
unset K
The tail of the key skips the prefix that every key from that vendor shares. Every command below assumes $K is set.

One pasted key, eight places it can end up.
On your machine: history, transcripts, scrollback
1. Shell history. If you ran export OPENAI_API_KEY=... with the value typed out, it's in your history file. Bash writes to ~/.bash_history by default (bash manual); zsh writes wherever HISTFILE points, and saves nothing if it's unset (zsh docs).
grep -nF -- "$K" "$HISTFILE" ~/.bash_history \
~/.zsh_history 2>/dev/null
Fix, in this order: in the shell where you typed it, remove the entry from memory first (history -d <offset> in bash), or that shell writes it back when it exits. Close your other shells so none of them writes the old list back, then delete the line from the file in an editor. For next time, set HISTCONTROL=ignorespace in bash or setopt HIST_IGNORE_SPACE in zsh, and lines that start with a space are not saved (bash, zsh). Better still, set the variable with read -rs OPENAI_API_KEY && export OPENAI_API_KEY and the value never touches a command line.
2. The agent's local transcripts. Claude Code writes each session to ~/.claude/projects/<project>/<session>.jsonl and deletes them after cleanupPeriodDays, 30 days by default. Your typed prompts go to ~/.claude/history.jsonl, which is kept until you delete it, and large pastes go to ~/.claude/paste-cache/ (Claude Code docs). Codex keeps its state under CODEX_HOME, ~/.codex by default, including history.jsonl (Codex docs), and writes session rollout files unless you run it non-interactively with codex exec --ephemeral (Codex commands).
grep -rlF -- "$K" ~/.claude ~/.codex 2>/dev/null
Fix for Claude Code: claude purge ~/code/app --dry-run shows what it would delete for that project (transcripts, auto memory, matching lines in history.jsonl), and the same command without --dry-run asks once and deletes. paste-cache/ isn't on purge's list, and it leaves backups/ alone, so delete any files the grep found in those two by hand. Lower cleanupPeriodDays, or set CLAUDE_CODE_SKIP_PROMPT_HISTORY to stop writing transcripts and prompt history at all (Claude Code docs).
For Codex, codex delete removes a session's transcript, while codex archive only hides it from the picker (Codex commands), and persistence = "none" under [history] in config.toml stops history.jsonl (Codex docs).
3. Terminal scrollback. If the agent ran env or printed a config, the key sits in the scrollback of that pane. In tmux, capture-pane -S - starts at the beginning of the pane's history and -p prints it, so you can count hits:
tmux capture-pane -p -S - -t <pane> | grep -cF -- "$K"
tmux clear-history -t <pane>
Outside tmux, use your terminal's clear-scrollback command. Remember that anything the agent's tool printed is also in its transcript, which is item 2 again.
In the repository: env files, fixtures, MCP configs
4. A committed .env or test fixture. The agent wrote the key into a fixture to make the test pass, or a .env got staged with everything else. git log -S finds commits that add or remove an exact string (git docs). Gitleaks finds keys by pattern, including the ones you forgot: gitleaks git scans history through git log -p, and gitleaks dir scans files on disk, so it catches the fixture you haven't committed yet.
git log --all -S "$K" --oneline
gitleaks git -v --log-opts="--all" .
gitleaks dir -v .
Fix: the old key is already revoked. If you still need it out of history, GitHub's guide uses git-filter-repo --sensitive-data-removal, and warns that forks, other people's clones, cached views and pull requests that reference the commit can still hold it. GitHub Support removes cached views and PR references only after every other ref is clean, and only when rotating the credential can't mitigate the risk (GitHub Docs).
5. An MCP server config. claude mcp add --header "Authorization: Bearer ..." with a literal token stores it in ~/.claude.json for local and user scope, and project scope writes .mcp.json, which the docs tell you to commit (Claude Code MCP docs). Codex reads MCP servers from ~/.codex/config.toml or a project's .codex/config.toml (Codex MCP docs).
grep -nF -- "$K" ~/.claude.json .mcp.json \
~/.codex/config.toml .codex/config.toml 2>/dev/null
Claude Code also keeps the five newest older copies of ~/.claude.json in ~/.claude/backups/, which item 2's grep already covers. Fix: reference variables instead of values. .mcp.json expands ${API_KEY} in headers, env and url (Claude Code MCP docs), and Codex has bearer_token_env_var and env_vars to read a token from your environment rather than the file (Codex MCP docs).
On GitHub: CI logs and screenshots
6. A CI log. GitHub masks registered secrets in logs, but redaction "largely relies on finding an exact match," is "not guaranteed," and only covers secrets the job used (GitHub Docs). A key the agent hard-coded into a fixture is not a registered secret, so a failing test that prints the request prints the key in full.
gh run list
gh run view <run-id> --log | grep -cF -- "$K"
gh run view --log prints the full log of a run. Fix: GitHub's advice when a secret shows up in a log is to delete the log and rotate the secret (GitHub Docs). gh run delete <run-id> deletes the whole run.
7. A PR screenshot. This is the one no grep finds. You pasted a screenshot of the failing request into the PR, and the header was in it. In public repositories, uploaded files "can be accessed without authentication"; in private ones, anyone with repo access can open them (GitHub Docs).
gh pr view <number> --comments
gh pr view --comments shows the pull request's comments, so you can open every image link and look. Fix: edit the comment to drop the image, then delete the old revision too, because anyone with read access can see a comment's edit history; open edited, pick the revision, then Options and "Delete revision from history" (GitHub Docs). The docs don't say editing deletes the uploaded file itself, which is one more reason to rotate.
In the agent's own memory: rules and notes
8. A memory or rules file. You told the agent "use this key for the staging tests," and it saved that as a note, or a teammate added it to the rules file so every session would have it. Claude Code reads CLAUDE.md and CLAUDE.local.md from the project (and can read AGENTS.md in place of CLAUDE.md), and keeps auto memory in ~/.claude/projects/<project>/memory/, which the 30-day sweep does not delete (Claude Code docs).
grep -rnF -- "$K" CLAUDE.md CLAUDE.local.md AGENTS.md \
.claude .codex 2>/dev/null
Item 2's grep already walks ~/.claude, auto memory included. Fix: delete the line, and if the file is committed, it's item 4 again.
Where Hermes IDE fits, and where it doesn't
I build Hermes IDE, so weigh this part with that in mind. It runs Claude Code, Codex and other CLI agents each in its own real terminal, and those terminals offer ghost-text suggestions from your command history as you type (README).
A history-based suggestion is one more place a key can come back. Since 2.3.2, released October 13, 2026, what you type at a hidden password prompt (sudo, ssh, a script asking for a passphrase) is never remembered or suggested. Commands that hold a secret, such as setting an API key or token, passing a password, an auth header or a URL with a password in it, are never shown as a suggestion or as faded text after the cursor (release notes).

Since 2.3.2, Hermes never offers a command that holds a secret back as a suggestion.
The same release notes say what it doesn't do: "Your shell's own history is not changed." Hermes keeps the key out of its suggestions, but ~/.zsh_history still has the line, so item 1 still applies to you. Hermes also has memory pins, facts saved for a project that reach every later session's agent, so the rule from item 8 applies there too: don't pin a key.
It's free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. It isn't affiliated with Nous Research, Anthropic, OpenAI or any agent vendor. Download it here, or read the source on GitHub.
If this was useful
If you're thinking about pointing a retrieval pipeline at your agent transcripts or your repo, everything on this list goes into the index with them, keys included. My RAG Pocket Guide covers retrieval, chunking and reranking for production, so you decide what an index should hold before you build it. It's one of 8 short books in the Pocket Guides for Developers series, covering agents, RAG, prompts, observability, events, databases and system design: the whole series is here.
來源:DEV Community · dev.to
