Do coding agents keep your API keys in their transcripts?

GuidesJohn Silver12 min read

Yes, they can. Claude Code, Codex CLI, Cursor and OpenCode keep a record of your conversations, and a key that passes through a tool call, such as a file read or a command that prints the environment, can end up in that record. Claude Code's documentation says it plainly: its files are plaintext, and anything that passes through a tool is written to a transcript on disk.

This guide covers where each of the four agents stores its history, what each vendor documents about retention and uploads, what zn-gate does about it and what it does not, and a checklist for cleaning up. Each claim about a vendor below links to that vendor's own page. Where we found nothing in the documentation, we say so instead of guessing.

Why would a key end up in a transcript?

An agent works by calling tools: read a file, run a command, search a folder, call an MCP server. The result of each call goes back to the model, and the agent keeps a log of the conversation so that you can resume it later. If the result contains AWS_ACCESS_KEY_ID=... because the agent read a .env file or ran env, the key is in the model's context and in the log.

So there are two copies to think about. One is on your disk, in the agent's history folder. The other is wherever the model provider processes and may retain your requests. Cleaning the first does not touch the second.

Where does each agent store its history?

Agent Local storage, as documented Documented control
Claude Code ~/.claude/projects/<project>/<session>.jsonl, plaintext, with large tool outputs in a tool-results/ folder cleanupPeriodDays, 30 days by default, minimum 1, CLAUDE_CODE_SKIP_PROMPT_HISTORY, claude purge
Codex CLI Under CODEX_HOME (default ~/.codex): config, auth, logs and sessions history.persistence, history.max_bytes, codex exec --ephemeral
Cursor Not stated in the pages we read Privacy Mode (server side)
OpenCode ~/.local/share/opencode/, session data in project/<project-slug>/storage/ Not stated in the pages we read

Claude Code

Claude Code's application data documentation says that the files under ~/.claude are plaintext and that anything that passes through a tool is written to a transcript on disk: file contents, command output and pasted text. The full conversation is in projects/<project>/<session>.jsonl, with every message, tool call and tool result. Subagent transcripts have their own folder, and large tool outputs are spilled into a tool-results/ folder next to the session.

Claude Code deletes these files once they are older than cleanupPeriodDays. The default is 30 days and the minimum is 1; the documentation says that setting 0 fails with a validation error. The data usage page repeats the 30-day default for local caching and points to the same page for how to clear it. It also notes that transcripts of sessions started or most recently continued in Claude Desktop or Cowork are exempt from that limit by default.

To shorten the window, set a lower value in ~/.claude/settings.json:

{
  "cleanupPeriodDays": 1
}

The same page (plaintext storage) lists ways to keep less. Setting the CLAUDE_CODE_SKIP_PROMPT_HISTORY environment variable skips writing transcripts and prompt history in any mode. In non-interactive mode you can instead pass --no-session-persistence with -p. claude purge deletes the transcripts, memory and prompt history that Claude Code holds for one project, after showing the plan and asking for confirmation. The files are also ordinary files, so you can delete them yourself.

Prompt history is a separate file. ~/.claude/history.jsonl keeps every prompt you typed, and the documentation says the age-based cleanup does not remove those entries unless the HIPAA configuration applies. A key pasted into a prompt can therefore still be there after the transcripts are gone.

Codex CLI

OpenAI's environment variables page says CODEX_HOME sets the root for Codex state, including config, auth, logs, sessions, skills and package metadata, and that the default is ~/.codex. The configuration reference has two history settings: history.persistence ("Control whether Codex saves session transcripts to history.jsonl", with the values save-all or none) and history.max_bytes, which caps the history file by dropping the oldest entries.

# ~/.codex/config.toml
[history]
persistence = "none"

For automation, the command reference lists codex exec --ephemeral: "Run without persisting session rollout files to disk."

What we did not find in the pages we read: a retention period for session files, or the exact subfolder layout under sessions. If you need that detail, look at your own ~/.codex folder.

Cursor

Cursor's help page on privacy and its data use overview describe what happens on the server side, and neither says where chat history is stored on your computer or how to delete it. That is a gap in what we could verify, not a statement that nothing is stored. Check the application's own settings for history controls.

The server side is documented. With Privacy Mode on, Cursor says your data is not used for training, and that it has zero data retention agreements with its model providers, with an exception: if your prompts or conversations trigger abuse detectors, the data may be stored for investigation. With Privacy Mode off, the overview says Cursor may keep and use codebase data, prompts, editor actions and code snippets to improve its AI features. Requests go through Cursor's backend even when you use your own API key, and the help page says zero data retention does not apply in that case: your data follows your provider's privacy policy, and Cursor temporarily caches file contents on its servers to reduce latency. The overview gives no retention periods for Cursor's own storage.

OpenCode

OpenCode's troubleshooting page lists the locations. Logs are in ~/.local/share/opencode/log/. Session data is in the project/ folder under ~/.local/share/opencode/: in <project-slug>/storage/ when the project is inside a Git repository, and in global/storage/ otherwise. On Windows the base folder is %USERPROFILE%\.local\share\opencode.

The current OpenCode documentation that we could open does not describe session sharing or a setting for it. If you have shared a session, check your version's own help for how to remove the link.

We did not find a documented retention period for the local session data.

Do the agents upload the transcript?

For the model call, yes by design: the agent sends your prompts and the tool results to the model provider, and that is how the model sees a key in the first place. What the vendor keeps afterwards depends on the product and the plan.

For Claude Code, the data usage page says the data sent includes all user prompts and model outputs, encrypted in transit. Anthropic's retention depends on the account: for Free, Pro and Max, 5 years if you allow data use for model improvement and 30 days if you do not; for Team, Enterprise and API, 30 days by default. Zero data retention is available for Claude Code on Claude for Enterprise, enabled per organization for qualified accounts.

The same page lists the cases where a transcript is sent on purpose. Running /feedback (and /bug or /share) sends a copy of your conversation history, and those reports are retained for 5 years. If you answer Yes to the optional "Can Anthropic look at your session transcript" follow-up after a session survey, the transcript is uploaded; known API key and token patterns are redacted before upload, other content is not. You can turn the feedback command off with DISABLE_FEEDBACK_COMMAND=1 and the survey with CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY=1.

For Codex CLI, the documentation pages we read do not describe uploads of local session files, and we are not stating what OpenAI retains on its servers. That depends on how you sign in and on your plan, so read OpenAI's own terms for your account. For Cursor and OpenCode, see the sections above.

What does zn-gate change, and what does it not?

zn-gate is a local layer that sits between your agent and the tools it uses. When a tool returns output, zn-gate replaces secrets in it, such as AWS keys, GitHub tokens, JWTs, private keys and passwords in database URLs, with markers like __ZN_AWS_KEY_1__ before the model sees the result. Because the transcript records what the model was shown, a new transcript holds the marker instead of the value. A secret pasted into a prompt is blocked instead of sent.

In Claude Code, Codex CLI and OpenCode, zn-gate scrubs the output of the agent's own tools. In Cursor, only MCP tool output is scrubbed. Shell output and file contents cannot be rewritten there, so values in files the agent reads, or that commands print, can reach the model.

Limits to keep in mind, from the threat model of zn-gate:

  • It does not clean transcripts that were written before you installed it. Those files still hold whatever was in them.
  • It does not control what an agent vendor stores on its servers. It reduces what is sent, and it cannot recall what was sent before.
  • Codex cannot rewrite tool output, so zn-gate replaces the result the model sees. Codex's own session log in ~/.codex/sessions keeps the original output.
  • In Claude Code, the transcript holds the scrubbed tool output, but a prompt that zn-gate blocked stays in it in clear text, as Claude Code's own documentation says. The command shown in a confirmation dialog when zn-gate restores a value for a local command is also recorded there.
  • OpenCode writes live output of a running command before zn-gate can see it, so that part stays in OpenCode's local database.

What should you do if a key may already be in a transcript?

  1. Rotate the key. Create a new one at the provider and revoke the old one. Deleting a file does not undo a copy that was sent to a model or included in a backup, so this step comes first.

  2. Find the transcripts that hold it. Search the history folders for the key prefix. For example, on macOS or Linux:

    grep -rl "AKIA" ~/.claude ~/.codex ~/.local/share/opencode 2>/dev/null

    Replace AKIA with the prefix of the key you are looking for. Use a prefix, not the whole key, so that the key does not land in your shell history.

  3. Delete what you do not need. Remove the matching session files from the folders listed above, or set cleanupPeriodDays to a low value for Claude Code and persistence = "none" for Codex. For Cursor, use the application's own history controls, since we found no documented path. For Claude Code, claude purge also removes the project's lines in history.jsonl.

  4. Check the other copies. Look at backups, cloud sync of your home folder and any session you shared. If you shared an OpenCode session, remove the link as your version's help describes. In Claude Code, /feedback reports are held by Anthropic as described above.

  5. Stop new copies. Move the values out of the places the agent reads, and put a scrubber in the path.

How do I stop new keys from reaching transcripts?

Install zn-gate and move your .env values into its local vault. The file keeps markers, so a read of it shows no value:

npx -y zn-gate init
npx -y zn-gate unlock
npx -y zn-gate env shadow .env
npx -y zn-gate check

init asks before each change and sets up Claude Code, Codex CLI, Cursor and OpenCode where it finds them. unlock creates the vault the first time. env shadow replaces the real values in .env with markers such as __ZN_STRIPE_KEY__ and keeps an encrypted copy that zn env unshadow can restore. check tests the protection with a fake secret in a temporary folder and tells you which agent is not protected. For the details of the setup, see how to stop Claude Code, Codex CLI, Cursor and OpenCode from reading your .env with one command.

What does this guide not cover?

  • Paths and settings change between versions of each agent. The statements above come from the vendors' documentation at the time of writing, and the file layout inside each folder can differ in your version.
  • We did not test deletion on each platform, and we did not verify what the vendors do with deleted data on their servers.
  • Cloud sessions, chat products and other editors are out of scope. zn-gate does not cover Claude Code sessions in the cloud, Cowork or chat.
  • A scrubber works on formats it recognizes. A secret in a format it does not know can pass through, which is why rotation is the first step after a leak.

Frequently asked questions

Do coding agents save my API keys in their conversation history?

They can. Claude Code's documentation says anything that passes through a tool is written to a plaintext transcript on disk, including file contents and command output. Codex CLI documents that it keeps session files under CODEX_HOME, and OpenCode documents a local session folder. Cursor's pages that we read do not say where chat history is stored locally.

Where does Claude Code keep its transcripts and for how long?

In ~/.claude/projects/, as one .jsonl file per session. Claude Code deletes them after cleanupPeriodDays, which is 30 days by default, and the minimum is 1.

How do I stop Codex CLI from saving history?

Set persistence to none under the [history] table in config.toml to stop writing history.jsonl, and run codex exec with --ephemeral so a non-interactive run does not write session rollout files. The documentation does not list a retention period for interactive session files.

Does zn-gate clean the transcripts that already exist?

No. zn-gate replaces secrets in tool output with markers before the model sees them, so new transcripts hold markers instead of values. It does not rewrite files written before you installed it, and it does not control what an agent vendor stores on its servers.

What should I do if a key reached a transcript?

Rotate it at the provider first, because a deleted file does not undo a copy that was sent to a model or backed up. Then delete the transcripts that hold it and move the values of your .env into the vault with npx -y zn-gate env shadow .env.

Does zn-gate protect Cursor in the same way as the other agents?

Not fully. In Cursor, zn-gate scrubs MCP tool output only. Shell output and file contents cannot be rewritten there, so keep the values out of the files with npx -y zn-gate env shadow .env. A secret pasted into a prompt is blocked in the four agents.


To try it on your own agents, run npx -y zn-gate init (MIT, local, no account), then npx -y zn-gate check. The code is on GitHub, and Install covers each agent. The routes a shell command can use to reach a protected file are in how a coding agent could read your .env file indirectly.