AI Assistants
Claude Desktop Conversations
Overview
Claude Desktop shows the claude.ai web interface inside Electron, so conversations are cached the way a browser would cache them. Two independent local sources hold message content:
- The IndexedDB conversation store, a LevelDB database whose values are serialized with the V8 structured-clone format.
- The Chromium HTTP cache, which keeps cached
chat_conversationsAPI responses as compressed JSON.
A third source, Cowork agent transcripts, is covered in Cowork Sessions.
Conversation history is authoritative on the server. These local stores are caches, so they hold a window of recent or recently opened conversations, not the account history. On the one reference host studied, the IndexedDB store held 16 conversations and the HTTP cache held roughly 200 cached conversation responses. Treat those numbers as an illustration, not an expectation.
IndexedDB Conversation Store
File Locations
| Path | Notes |
|---|---|
~/Library/Application Support/Claude/IndexedDB/https_claude.ai_0.indexeddb.leveldb/ | LevelDB database (legacy location) |
~/Library/Application Support/Claude/IndexedDB/https_claude.ai_0.indexeddb.blob/ | External blob files for large values |
~/Library/Application Support/Claude/WebStorage/<bucket>/IndexedDB/indexeddb.leveldb/ | Storage-bucket location in newer builds |
~/Library/Application Support/Claude/WebStorage/<bucket>/IndexedDB/indexeddb.blob/ | Blobs for the bucket |
~/Library/Application Support/Claude/WebStorage/QuotaManager | SQLite; its buckets table maps a bucket to an origin |
Whether a given host uses the legacy path, the bucket path, or both depends on the app version. Whether the two represent a migration or coexistence is not established, so check for both. Resolve a bucket directory to its origin through the QuotaManager database rather than by guessing.
Copy the whole LevelDB directory (.ldb, .log, MANIFEST-*, CURRENT) before parsing. If the app is running the directory holds a LOCK file; work from a copy.
Format
IndexedDB on Chromium is stored in LevelDB. Keys carry prefixes that encode the database, object store, and index IDs. Values are wrapped: a V8 serialization header (0xFF version byte), a Blink envelope, and optionally Snappy compression or a pointer to an external blob. Blob files are located by number under the .blob directory in <db_id>/<hex byte>/<hex number> form. LevelDB SSTable blocks are Snappy-compressed.
Databases
| Database | Contents |
|---|---|
claude-conversation-store | Conversation metadata (meta store) and message trees (trees store) |
keyval-store | General key-value cache |
claude-notifications | Notification state |
claude-device-binding | Device binding data |
omelette-fs-access | File-system access handles |
frame-* | Frame caches |
Only claude-conversation-store carries message content. The others are useful to inventory (names and record counts) but were not found to hold conversation text.
meta Store
One record per conversation.
| Field | Description |
|---|---|
accountUuid, orgUuid | Account and organization identifiers |
conversationUuid | Conversation identifier |
product | hub (chat) or cowork |
messageCount, approxBytes | Size of the cached conversation |
conversationUpdatedAt | Last server-side update |
fetchedAt | When the app last fetched it |
writtenAt | When it was last written to the store |
lastOpenedAt | When the user last opened it |
deletedAt, tombstone | Set when the conversation was deleted |
All of these are Unix milliseconds. A record with tombstone set marks a conversation the user deleted. The metadata that survives can still show that the conversation existed and when it was last opened.
trees Store
One record per cached conversation tree.
| Field | Description |
|---|---|
v | Schema version (observed: 2.2) |
product, sessionMarker | Product and session marker |
accountUuid, orgUuid, conversationUuid | Identifiers |
fetchedAt, writtenAt, conversationUpdatedAt | Unix milliseconds |
tree.kind, tree.treeSize, tree.headSeq, tree.floorSeq, tree.hasOlder | Tree window: hasOlder indicates earlier messages exist on the server but are not cached |
tree.contentShape | Summary: model, total text characters, counts of thinking and tool-use blocks |
tree.events[] | System and environment-manager log events |
tree.messages[] | The cached messages |
Each message carries:
| Field | Description |
|---|---|
uuid, parent_message_uuid, index | Position in the tree |
sender | human or assistant |
created_at, updated_at | Message timestamps |
stop_reason, input_mode | How the assistant turn ended; how the human turn was entered |
nOptions, selectedOption, siblingUuids | Regenerated and edited branches |
attachments[], files[], files_v2[] | Attachments and uploaded files (file name, kind, UUID, size) |
sync_sources[], chat_feedback | Sync origins and thumbs feedback |
content[] | Typed content blocks |
Content blocks are text, thinking, tool_use (id, tool name, input), and tool_result (matching tool_use_id, error flag, text). Thinking blocks mean the assistant's reasoning text can be present locally.
parent_message_uuid forms a tree. When a user edits a prompt or regenerates a response, the older branch remains under a sibling. Reconstruct the conversation by walking from the selected option, and consider unselected siblings as evidence of prompts the user abandoned.
HTTP Cache
File Locations
| Path | Notes |
|---|---|
~/Library/Application Support/Claude/Cache/Cache_Data/ | Default cache |
~/Library/Application Support/Claude/Partitions/*/Cache/Cache_Data/ | Per-partition caches |
Format
The cache is a Chromium Simple Cache: one file per entry, named with a 16-hex-digit hash and a _0 suffix (sparse _s files hold range data and are not useful here). Each entry file starts with a magic number, followed by the key and the response stream, with the pickled response metadata (status, headers, request and response times) near the end. The key looks like 1/0/_dk_<site> <site> <url>. Bodies are compressed according to Content-Encoding; the cached claude.ai conversation responses are usually zstd, and brotli and gzip also occur.
What the Cache Holds
The parseable prize is the conversation API response:
https://claude.ai/api/organizations/<org>/chat_conversations/<uuid>?...
Its JSON body yields the same message fields as the IndexedDB tree, with the request and response times from the cache entry as timestamps. Other cached claude.ai endpoints are worth indexing even when the body is not parsed, because the URL, status, size, and time show what the user did:
| URL pattern | Meaning |
|---|---|
/files/<uuid>/contents, /preview, /thumbnail | Uploaded or generated files |
/wiggle/download-file | File downloads |
/artifacts/<uuid>/versions | Artifact versions |
/projects..., /memory | Projects and memory |
/cowork/..., /v1/code/sessions... | Cowork and Code sessions |
/plugins/..., /skills/<id> | Plugins and skills |
api.anthropic.com/api/directory/servers | Connector directory |
.../squirrel/update | Update checks |
By default a collector should restrict body parsing to claude.ai, a.claude.ai, api.anthropic.com and *.claudeusercontent.com hosts, since the cache also holds unrelated third-party content.
Deduplication and Provenance
The same message commonly appears in both stores. macfor merges them per message and records provenance:
sourceisindexeddborhttp_cachefor the primary record.also_seen_inlists the other sources that held the same message.content_differsflags a message whose text is not identical across sources.
A difference between sources is worth examining: it can indicate an edit, a regeneration, or a cache that was captured at a different moment.
Recovered Values
LevelDB does not overwrite in place. A changed or deleted key leaves the old value in an earlier SSTable or in the write-ahead .log until compaction removes it. Reading every sequence number rather than only live entries can therefore surface:
- Earlier versions of a conversation tree (superseded values)
- Trees for conversations that were deleted (deleted values)
macfor flags these records recovered: true. In a report, state clearly that a value is recovered and not live, and that its timestamps describe the stored content, not when it was removed. How long old values persist depends on compaction, so absence of recovered values proves nothing.
Messages whose body lives in an external blob that cannot be found are emitted with blob_missing: true.
Timestamps
| Source | Field | Format |
|---|---|---|
IndexedDB meta / trees | fetchedAt, writtenAt, lastOpenedAt, deletedAt, conversationUpdatedAt | Unix milliseconds |
| IndexedDB message | created_at, updated_at | ISO 8601 string |
| HTTP cache | Request and response times | Chromium time: microseconds since 1601-01-01 UTC |
| HTTP cache | Date response header | HTTP date |
-- Chromium microseconds since 1601 to a readable UTC time
SELECT datetime(:chromium_us / 1000000 - 11644473600, 'unixepoch');
Investigation Notes
- Establish the account.
accountUuidandorgUuidtie records to an account. The same UUIDs appear in thelocal-agent-mode-sessions/path and inconfig.json. - Cached is not complete.
hasOlderandmessageCountagainst the number of cached messages show how much of a conversation is missing locally. - Attachments.
files[]names uploaded files and their sizes. The file content may be recoverable from the cache/files/responses or from a Coworkuploads/folder. - Deletion. A
tombstoneanddeletedAtshow a deliberate delete. Look in recovered values for the content. - Redaction. Conversation text is user content. macfor's
skip_contentoption collects structure, counts and tool names without message text.
What macfor Collects
The ai.claudedesktop plugin emits claude_conversation, claude_message, claude_conversation_event, claude_idb_inventory, and claude_api_cache_entry records. IndexedDB, blob, cache, and QuotaManager files are copied byte-for-byte as original evidence.
{
"type": "claude_message",
"source": "indexeddb",
"recovered": false,
"conversation_uuid": "00000000-0000-4000-8000-000000000001",
"product": "hub",
"message_uuid": "00000000-0000-4000-8000-0000000000a2",
"sender": "assistant",
"created_at": "2026-08-02T10:15:03Z",
"stop_reason": "end_turn",
"text": "Example response text",
"tool_names": [],
"also_seen_in": ["http_cache"],
"schema_version": "2.2"
}
All values above are illustrative.