WAL analysis

What Is a Write-Ahead Log Viewer?

A write-ahead log viewer is a tool for inspecting SQLite -wal files so an examiner can review earlier versions of database pages, identify transaction boundaries, and recover records that no longer appear in the live database.

The live database shows the current state. The write-ahead log holds the states that came before it, and often holds them for a long time.

The mechanism

Why does the WAL matter in forensics?

In WAL mode the database file is deliberately not kept current. When a page changes, the complete new version of that page is appended to the -wal file as a frame. The database file waits for a checkpoint to catch up.

The purpose is to stop readers and writers blocking each other. The forensic consequence is that a page changed ten times exists on disk as ten complete versions, in the order they were written.

That is why opening the live database and reading the tables answers a narrower question than most people assume. The tables show the current state. The WAL holds the states that produced it.

It is where you go to answer questions the live database cannot:

  • Was this record ever written, and what did it look like before?
  • Did the record exist only in the WAL and never reach the database file?
  • Was it updated or removed later, and what was there first?
  • Why does the live database not match what the application was doing?
Byte level

What is inside a WAL frame?

A -wal file is a 32 byte header followed by frames. Each frame is a 24 byte header plus one complete page image.

OffsetSizeMeaning
04Page number
44Database size in pages, if this frame commits. Otherwise zero.
84Salt-1, copied from the file header
124Salt-2, copied from the file header
168Two running checksums

The field at offset 4 is the one people skip. It reads zero on almost every frame. When it holds a value, that frame ends a transaction, and the value is the size of the database afterwards. It is the only structural marker of a transaction boundary in the file, which makes it the only way to tell which frames belong together.

Nine frame headers, salts and checksums trimmed
frame 1 00 00 00 04 00 00 00 00
frame 2 00 00 00 07 00 00 00 00
frame 3 00 00 00 02 00 00 00 0C
frame 4 00 00 00 04 00 00 00 00
frame 5 00 00 00 09 00 00 00 0C
frame 6 00 00 00 04 00 00 00 00
frame 7 00 00 00 07 00 00 00 00
frame 8 00 00 00 03 00 00 00 00
frame 9 00 00 00 02 00 00 00 0C

Frames 1 to 3 are one transaction, because frame 3 commits. Frames 4 and 5 are a second. Frames 6 to 9 are a third. Three user actions, nine frames.

Now look at page 4. It appears in frames 1, 4 and 6. That is three versions of the same page, and only the last one is current. The other two are history, and they are readable.

Recovery

What happens when a WAL is checkpointed?

A checkpoint copies frames from the WAL back into the database file. After that the WAL can restart, meaning the next write begins again at frame 1.

The salt values at offsets 8 and 12 exist for integrity. If a reader is working through the file and it restarts underneath, the salts stop matching and the reader knows to stop rather than stitch together pages that never coexisted.

When the WAL restarts, both salts change. Frames further down the file keep the previous pair, because nothing goes back to clear them, and the file is usually not shortened. SQLite stops reading at the first mismatch. Everything past that point is invisible to the engine.

Current header salts, and a frame from the previous generation
header offset 16 00 00 00 05 salt-1
header offset 20 9A 3F C1 07 salt-2 frame 41 00 00 00 04 00 00 00 00 00 00 00 04 1E 77 B2 D9

Salt-1 is 4, not 5. That frame belongs to the previous generation of this WAL. It is a complete page image from before the last checkpoint, sitting on disk in full, and it is one of the better sources of historical state in the whole format.

A second boundary marker

The frame checksums are cumulative, each computed over the running value from the frame before. The point where the chain breaks is another way to find where the current generation ends and the leftovers begin.

A common misreading

Why is the WAL not a timeline?

Because there is no timestamp anywhere in the format. Not in the file header, not in any frame header. Any time you have dated something out of a WAL, that came from a value inside a page rather than from the log itself.

Frame order is the order SQLite flushed pages, which is related to the order of events and is not the same thing. Sending one message can write the table leaf, an index page, sqlite_sequence and a freelist page. Four frames, one action. Meanwhile a background task the user never triggered writes frames in between.

Present frame order to a court as the order of events and you are presenting the order in which pages happened to be flushed. What the file gives you reliably is states, not moments.

Read it as

A sequence of states. Page 4 looked like this, then like this, then like this. What happened between them, and when, has to come from inside the pages.

What the WAL proves

That a page held certain content at some point before it held its current content.

What it does not prove

When that happened, or that adjacent frames belong to the same user action.

Handling

How do you avoid destroying it?

By never opening the original with a read-write connection.

A standard SQLite connection can checkpoint the WAL when it closes, merging its contents into the database file, and depending on journal mode it can delete the -wal and -shm files entirely. Everything described on this page can disappear in the moment a handle closes, including from a tool that only intended to look.

Copy the database, the -wal and the -shm together, and work on the copy. If you must touch the original, open it read only.

Why this matters more than technique

Nothing warns you when it goes wrong. The file is simply smaller than it was, and the frames you needed are gone. It is worth building the habit before you need it.

The -shm file is the index SQLite builds so readers can find the newest frame for a page without scanning the whole file. It is derived rather than primary and is rebuilt when needed, so treat it as a signal about state rather than as evidence in itself.

Frame by frame

See each version of a page in write order, not only the current state.

Transaction boundaries

Group frames by commit, so you know which writes belonged together.

Frames past the salt change

Reach the page images from earlier generations that SQLite itself will not read.

Records in table context

Interpret a recovered page against the schema rather than as loose bytes.

Down to the bytes

Move from a recovered record to the offset it came from without leaving the case.

Read-only by default

Open the evidence without the risk of checkpointing it on close.

Requirements

What do you need from a WAL viewer?

Most database browsers were not built for this. They open the database, silently merge the WAL, and show you the result. That is correct behaviour for a developer tool and the wrong behaviour for evidence.

A forensic WAL viewer has to do the opposite: leave the source untouched, expose the frames rather than the merged result, and keep each recovered record connected to the frame and offset it came from.

The workspace

Where does Elusive Data Firefly fit?

Firefly treats WAL analysis as part of the case rather than as a separate artifact. Records recovered from WAL frames appear alongside live records and alongside records from rollback journals, freelist pages and freeblocks, each labelled with the mechanism that surfaced it.

That labelling is the point. A record recovered from a freelist page arrives whole. A record recovered from a freeblock has lost its rowid. A record recovered from an earlier WAL frame is a previous state rather than a deletion. Knowing which one you are holding changes what you can say about it.

Evidence handling is read-only throughout, with SHA-256 evidence-set validation, and everything runs locally. Where the data supports it, a finding keeps its source location and that context carries into an interactive report.

What it does not do

Firefly is not an extraction tool. It starts where decoded output stops and assumes you keep the platform you already use. SQLite Visualizer is now part of Firefly.

The broader framework is on SQLite Forensic Analysis. On validating what a viewer shows you, see When a Viewer Is Not Enough.

Training

Where do you learn the method?

Everything on this page is format knowledge rather than tool knowledge. It transfers to whatever platform is on your desk, and it is taught in two places depending on how much of it you want.

Focused · 5 CPE · On demand

WAL Frames and SHM Index

The write-ahead log on its own, taught at the level this page describes. Built for examiners who need pre-checkpoint recovery now rather than a full course.

  • Read a frame header and locate transaction boundaries from the commit field
  • Recover complete page images written before the last checkpoint
  • Use the salt values to find frames SQLite itself will not read
  • Tell an earlier state apart from a deletion, and say which you are looking at
  • Work with the SHM index and understand what it does and does not evidence
  • Handle the file set without checkpointing away what you came for
Complete · 24 CPE · Certificate

Certified SQLite Forensics

The whole format, with WAL and SHM as one part of it. Guided and self-paced, with real training databases you download and work on in your own tools.

  • File headers, page structure, records, serial types and varints
  • B-tree navigation, freelists, freeblocks and overflow pages
  • WAL and SHM analysis, and recovery of deleted records
  • Validating incomplete or unsupported tool output against the source
  • Byte-level exercises after each concept, then CTF style challenges
  • A repeatable way to explain how a finding was reached

No coding required. Written for investigators rather than developers. Three months of access, course related email support and a one hour instructor session.

Certified SQLite Forensics also runs live online and on-site by arrangement. See the formats.

Last updated 6 September 2026