Blog
·6 min

Where OpenCode Stores Your Session History

OpenCode keeps every session in one SQLite database under ~/.local/share/opencode. Here is the schema, the queries that let you read your own history, and what to do about the fact that it is one file.

OpenCode stores its history in a single SQLite database, which is the most readable arrangement of any tool in this category. You can query your own conversations with the sqlite3 binary that ships with your operating system, no export required. That is genuinely rare, and worth using.

On the machine this article was written on, that database is 1.4 GB and holds 392 sessions, 13,799 messages and 63,413 message parts. One file. No second copy.

The exact location

OpenCode follows the XDG data directory convention, with fallbacks:

# Linux and macOS (the usual one)
~/.local/share/opencode/opencode.db

# if XDG_DATA_HOME is set
$XDG_DATA_HOME/opencode/opencode.db

# macOS alternative layout
~/Library/Application Support/opencode/opencode.db

# Windows
%LOCALAPPDATA%\opencode\opencode.db

You will usually find opencode.db-wal and opencode.db-shm beside it — SQLite's write-ahead log and shared-memory index. They matter for backups: copy the database without them while OpenCode is running and you can capture a state that is missing the most recent writes.

A storage/ directory sits alongside, holding session diffs and migration state. It is small — under 6 MB on this machine against 1.4 GB of database — but it is the part that records what changed in your files, so include it in any copy.

Reading your own history

The schema is straightforward: sessions contain messages, messages contain parts. Everything below is read-only and safe:

DB=~/.local/share/opencode/opencode.db

# what is in there
sqlite3 -readonly "$DB" ".tables"

# how much history you have
sqlite3 -readonly "$DB" "SELECT
  (SELECT COUNT(*) FROM session)  AS sessions,
  (SELECT COUNT(*) FROM message)  AS messages,
  (SELECT COUNT(*) FROM part)     AS parts;"

# the schema, when you want to write your own query
sqlite3 -readonly "$DB" ".schema session"
sqlite3 -readonly "$DB" ".schema message"

Because it is SQLite, everything you already know applies. You can join sessions to projects, count messages per day, export a conversation to JSON with the .mode json dot-command, or attach the database from a script. No product needs to grant you permission to read your own prompts — which, having looked at what the other tools in this category do, is not something to take for granted.

What actually goes wrong

  • It is one file, and it grows. 1.4 GB after a few hundred sessions is normal, and nothing prunes it. That number is the reason people eventually delete it.
  • A single file is a single point of failure. An unclean shutdown or a full disk can leave a database that will not open, and there is no second copy to fall back on.
  • Copying it live can produce a torn snapshot. If the WAL is not copied with it, the backup is missing whatever had not been checkpointed.
  • There is no export in the product. What you get out is what you query out — fine for you, useless for a colleague who does not know the schema exists.
  • It covers OpenCode alone. Claude Code, Cursor, Copilot, Codex and the rest each keep their own history in their own format, with their own rules for losing it.

How to back it up properly

Do not use cp on a live SQLite database. Use SQLite's own backup command, which produces a consistent copy while the file is in use:

DB=~/.local/share/opencode/opencode.db
DEST="$HOME/Backups/opencode-$(date +%Y-%m-%d)"
mkdir -p "$DEST"

# consistent copy, safe while OpenCode is running
sqlite3 "$DB" ".backup '$DEST/opencode.db'"

# the diffs live outside the database
cp -R ~/.local/share/opencode/storage "$DEST/"

# verify the copy actually opens
sqlite3 -readonly "$DEST/opencode.db" "PRAGMA integrity_check;"

That last line is the part people skip. A backup you have never opened is a hypothesis, and integrity_check turns it into a fact for the cost of one command.

If you want the same picture across every AI tool on the machine, promptwake doctor scans for AI coding history and reports what each tool is holding and what is inside a deletion window — via npx, no account, writes nothing and sends nothing anywhere.

Where the personal fix stops working

A verified nightly backup of opencode.db is a complete answer for one developer, and better than what most people running these tools have. It still leaves the team-scale problems untouched: the database is on one laptop, readable by one person, in a schema nobody else will learn, covering one tool out of the several your team actually uses. And a SQLite file on a personal machine is not evidence you could put in front of a customer's security questionnaire asking how AI-generated code is retained and reviewed.

Being able to query your own history is the good case. It is still one file, on one machine, that only its owner will ever open.

What to do this week

Run the three counts above so you know the size of what you are carrying, then set up the .backup command on a schedule you will keep. If the database is over a gigabyte, that is a year of engineering reasoning with no second copy.

If the conclusion is that the record belongs to the team rather than to whichever laptop produced it, that is the problem PromptWake exists for: it reads what OpenCode and the other tools already write to disk, keeps it locally by default, and on the paid tiers syncs prompt, response and resulting diff into one timeline the team can search. Set up the backup regardless.