Part of the AI coding agent security checklist

Where Claude Code keeps your conversation history

Every Claude Code session is saved on your machine as a plaintext JSONL file, and deleted after 30 days unless you change that. Where the files are, what is in them, how long they stay, and how to remove them.

$ ls -lt ~/.claude/projects/ | head

Checked against the Claude Code documentation and the reports under Sources. Published .

01 / Where

Where the files are

Claude Code stores each session as one file, ~/.claude/projects/<project>/<session-id>.jsonl. <project> is the working directory path with every character that is not a letter or a digit replaced by -, so a session started in /Users/you/work/my-repo is saved under ~/.claude/projects/-Users-you-work-my-repo/.

If the converted name is longer than 200 characters, Claude Code cuts it to 200 and appends a hash of the full path, so it stays within filesystem limits. On Windows, ~/.claude is %USERPROFILE%\.claude.

# projects, most recently used first
ls -lt ~/.claude/projects/ | head

# sessions in one project, newest first
ls -lt ~/.claude/projects/-Users-you-work-my-repo/ | head

# the project directory for the folder you are in
ls -lt ~/.claude/projects/"$(pwd | sed 's/[^A-Za-z0-9]/-/g')"/ | head

# how much is stored
du -sh ~/.claude/projects

The third command applies the naming rule above. For a path over 200 characters it will not find the hashed name; list the directory and look instead.

The session file is not the only thing a session leaves behind:

PathWhat it holdsAge limit
projects/<project>/<session-id>.jsonl The full conversation: every message, tool call and tool result. Swept
projects/<project>/<session-id>/subagents/ Transcripts of the subagents that session started. Swept
projects/<project>/<session-id>/tool-results/ Large tool outputs, spilled to separate files. Swept
file-history/<session-id>/ Copies of files taken before Claude edited them, used to rewind to a checkpoint. Swept
paste-cache/ The contents of large pastes. Swept
history.jsonl Every prompt you have typed, with its time and project path. Used for up-arrow recall and Ctrl+R search. Kept
/tmp/claude/response.md What /copy wrote on Claude Code 2.1.59 to 2.1.127: one fixed path for every user on the machine, readable by all of them. Kept

Paths that do not start with / are under ~/.claude/. If you set CLAUDE_CONFIG_DIR, all of it lives under that directory instead: with CLAUDE_CONFIG_DIR=~/.claude-work, transcripts are in ~/.claude-work/projects/. In current versions, pasted images and each session's scratchpad are not here at all. They sit under Claude Code's temp directory, such as /private/tmp/claude-<uid>/ on macOS.

Older builds left one more file outside both. On 2.1.59 to 2.1.127, /copy wrote the response to /tmp/claude/response.md, readable by everyone (0644) in a directory anyone could enter (0755). Another local user could read what you copied, secrets included, or plant a symlink there so that your next /copy overwrote a file of their choosing (CVE-2026-46406). 2.1.128 fixed it. Check for a copy an old build left:

claude --version              # 2.1.128 or later
ls -la /tmp/claude/           # owner, mode, and whether response.md is a symlink
rm /tmp/claude/response.md    # once you have looked

If response.md is a symlink, or /tmp/claude/ belongs to another user, someone set up the overwrite the advisory describes. Note where the link points before you remove it.

02 / Contents

What is in them

Everything the session saw. The contents of every file Claude read, the output of every command it ran, and the text you pasted. If the agent ran cat .env, the values are in the transcript.

None of it is encrypted. Claude Code's documentation says transcripts and history are not encrypted at rest and that OS file permissions are the only protection. Anything that can read your home directory can read them, including whatever backs it up or syncs it. On a shared machine, check its permissions with ls -ld ~/.claude; chmod 700 ~/.claude keeps other users out. To keep secrets out of the transcript in the first place, see how to stop Claude Code reading your .env file.

Format One JSON object per line, for a message, a tool use or metadata. The format is internal and changes between releases, so a script that parses its fields can break on any update. Treat the files as text, not as an API.
Read one /export copies the current conversation to the clipboard or saves it as a plain-text file, with tool output rendered as readable text. /export notes.txt skips the menu and writes the file.
From a script claude -p --resume <session-id> --output-format json "summarise what we changed" asks the session itself and returns JSON. A SessionEnd hook receives the transcript's path and can archive it.
# a readable copy of an older session: resume it, then export
claude --resume <session-id>
/export session.txt

# which files mention a value you are worried about
grep -rlsF 'first-8-chars' ~/.claude/projects ~/.claude/history.jsonl \
  ~/.claude/file-history ~/.claude/paste-cache

Search for the first few characters of a key, not the whole value, or the full secret ends up in your shell history as well. The recursive search covers subagent transcripts, tool-result files, edit snapshots and large pastes too.

Every tool call is in there with its arguments, so you can also read the transcripts back as an audit log of what the agent ran.

What malware looks for

File permissions keep other users out. They do not stop a program running as you. On 8 September 2026, Gen Digital reported that commodity infostealers now collect AI coding tools' local data: access and refresh tokens, credentials in MCP configs, prompt histories and conversation databases. It names Claude among the targets of the Remus and CallbackBeaver families, and shows an STG Stealer configuration that collects ~/.claude/.credentials.json and ~/.claude.json: the file Claude Code keeps its login in when it cannot use the macOS Keychain, and its config. The report says the data sits in predictable places, some of it in plaintext. ~/.claude/projects fits that description. Retention is a security setting as well as an audit one: what a stealer cannot find, it cannot take.

Keep less A cleanupPeriodDays that matches how far back you actually look.
Mask ranwhat clean --apply masks each credential it finds in the transcripts from the last 30 days (--days N for more). Delete ~/.ranwhat/backups once the result looks right: the backups hold the originals.
Purge claude project purge for projects you have finished.
Login Claude Code keeps its login in the macOS Keychain where it can. On Linux and Windows, and on a Mac when the Keychain rejects the write, it goes in ~/.claude/.credentials.json, protected by file permissions. That file is on the STG Stealer list above.

After an infection, rotate from a clean device: every credential the transcripts held, the keys in your MCP configs, and your AI services' sessions and API keys, not only browser passwords. ranwhat clean lists what the transcripts held, and rotate in its review session groups it by provider.

03 / Retention

How long Claude Code keeps them

30 days by default. The cleanupPeriodDays setting changes it: a whole number of days, minimum 1. Claude Code deletes anything older in a background sweep after a session starts, without a message, so an old session simply stops appearing in /resume.

~/.claude/settings.json
{
  "cleanupPeriodDays": 90
}
Longer Any larger number. The documentation suggests something like 3650 for long retention.
Shorter A smaller number means less sitting on disk. The documentation lists lowering it as a way to reduce exposure.
Zero 0 fails validation. It does not mean keep nothing.
Never swept history.jsonl and stats-cache.json stay until you delete them, so every prompt you have typed, in every project, is still there.
Desktop Transcripts of sessions you started or last continued in Claude Desktop or Cowork are kept at any age, unless you set desktopSessionCleanupPeriodDays in user or managed settings, or in a file passed with --settings. Each is then deleted once it is older than both limits. If managed settings set cleanupPeriodDays, that period applies to them instead. Claude Code v2.1.248 or later; earlier versions delete them after cleanupPeriodDays.
Paused If Claude Code cannot work out the retention period, it pauses the sweep rather than guess. A settings file that cannot be read or parsed is one cause, and shows a warning in /status. Runs with claude -p --bare skip the sweep.

To stop the files being written at all, set CLAUDE_CODE_SKIP_PROMPT_HISTORY=1. Sessions started with it write no transcript and no prompt history, and do not appear in --resume, --continue or up-arrow history. For a single claude -p run, --no-session-persistence skips the transcript.

CLAUDE_CODE_SKIP_PROMPT_HISTORY=1 claude
claude -p --no-session-persistence "run the tests and list the failures"
04 / Delete

How to delete them

claude project purge removes what Claude Code holds for one project. It prints the full plan first and asks before deleting anything. Start with a dry run.

claude project purge ~/work/my-repo --dry-run   the plan, nothing deleted
claude project purge ~/work/my-repo             the plan, then one y/N prompt
claude project purge ~/work/my-repo --yes       no prompt, for scripts
claude project purge ~/work/my-repo -i          step through item by item
claude project purge --all                      every project

Leave out the path to pick a project from a list. A path with no stored state is an error, with exit status 1.

Removes The project's transcripts and auto memory under projects/, its sessions' tasks/, debug/ and file-history/ entries, its lines in history.jsonl, and its entry in ~/.claude.json.
--all Deletes history.jsonl outright instead of filtering it.
Leaves Pasted images and session scratchpads, which live in the temp directory. The sweep still removes the images after cleanupPeriodDays; a purged session's scratchpad stays until you or the operating system clear it. shell-snapshots/ and backups/ are not per project, so it leaves those too and says so in the plan. And /tmp/claude/response.md, if a build before 2.1.128 ran /copy.
Costs Resume, continue and rewind for the deleted sessions, and the project's auto memory. New sessions are unaffected.

You can also delete the files by hand. The main pieces of one session are its transcript, its directory of subagent and tool-result files, and its edit snapshots. Its prompts stay in history.jsonl, which purge filters for you.

rm ~/.claude/projects/-Users-you-work-my-repo/<session-id>.jsonl
rm -r ~/.claude/projects/-Users-you-work-my-repo/<session-id>/
rm -r ~/.claude/file-history/<session-id>/

Do not delete these

~/.claude.json, ~/.claude/settings.json and ~/.claude/plugins/ hold your auth, preferences and installed plugins. ~/.claude/.credentials.json, where it exists, holds your login. And claude rm <id> is not a deletion of history: it removes a background session, but its transcript stays on disk and claude --resume can still open it.

05 / Before

Before you delete: see what is in them

Deleting a transcript removes your local copy of a secret. It does not un-send it. The value already went to the model as part of the conversation, and what the provider retains is set by your account's terms, not by the files on your disk. So the first job is a list of what to rotate.

For Anthropic accounts, the data usage page gives the retention: 30 days as standard for Team, Enterprise and API accounts, and 30 days or 5 years for Free, Pro and Max, depending on whether you allow your data to be used for model improvement.

$ uvx ranwhat check
Install

check reads the transcripts and changes nothing. It lists the risky actions the agent took, found by the same rules as ranwhat watch, and the credentials sitting in the files, as ranwhat clean finds them, each with the project it was found in. Rotate those. Then either purge the project, or keep the history and mask the values:

ranwhat clean           report, then a review session: mask 3, keep 3, rotate
ranwhat clean --apply   mask every value found, backups first

--apply replaces each value in place with <ranwhat:redacted:…>. It copies each file it changes to ~/.ranwhat/backups first, and parses the rewritten file back before it replaces the original. The backup holds the original values, so delete it once you have checked the result.

Reads The session files, ~/.claude/projects/*/*.jsonl, and the subagent transcripts under each session's subagents/. Not tool-results/, file-history/, paste-cache/ or history.jsonl. The grep above covers those.
Window --days, default 30. A transcript is read if it was written to inside the window, and the watch half then keeps only the actions that happened inside it. That matches Claude Code's default retention. If you raised cleanupPeriodDays or keep Desktop sessions, widen it: ranwhat check --days 365.
Moved config When CLAUDE_CONFIG_DIR is set, it reads $CLAUDE_CONFIG_DIR/projects instead. --root PATH reads any other directory.
Zero Read the top of the report. A run that reads nothing says that nothing was checked, rather than that nothing was found. That is not a clean machine: check the directory and the window.

To stop the values reaching a transcript in the first place, see Claude Code and .env secrets.