Your SQLite backup exited zero. Did it include the rows still committed only in the WAL?
WalProof is a zero-runtime-dependency CLI that rehearses an arbitrary SQLite backup command against a generated WAL-mode database while writes continue. It restores the result and produces machine-readable evidence for the exact failure that ordinary file checks miss: an openable, internally consistent backup that silently lost committed rows.
WalProof does not back up production data. It gives your backup command a disposable
{source} and {backup}, then judges what that command actually preserved.
In SQLite WAL mode, commits are appended to a separate write-ahead log and reach the main
database file later during a checkpoint. SQLite's own documentation therefore treats the
WAL as part of the database's persistent state. Copying only app.sqlite can produce a
file that opens and passes PRAGMA quick_check while still being stale. See the official
WAL documentation and
Online Backup API.
WalProof turns that warning into an executable regression test:
generated WAL database + concurrent writer
│
▼
your exact backup argv
│
▼
restore → identity + committed rows + sequence + payload checks
Install the released wheel with Python 3.11 or newer:
python -m pip install https://github.com/KanadeK/walproof/releases/download/v0.1.0/walproof-0.1.0-py3-none-any.whl
walproof demo --report-dir walproof-reportsThe demo intentionally runs two real child processes. A main-file copy must fail the committed-WAL check, the SQLite Backup API must pass, and only that pair makes the demo itself succeed:
[FAIL] unsafe-copy: ...
[PASS] backup-api: ...
[PASS] demo verdict
The exact event counts vary because the writer is live. The verdict does not depend on capturing writes that happened after the backup command started.
Create a strict version 1 plan. Each item is passed directly to the child process; no shell parses the command.
{
"schema_version": 1,
"name": "my-backup",
"backup_argv": [
"{python}",
"backup.py",
"{source}",
"{backup}"
],
"timeout_seconds": 30
}{source} and {backup} are required exact arguments. {python} is optional. Relative
paths resolve from the plan file's directory.
walproof rehearse plan.json --report-dir walproof-reportsThe repository includes both sides of the proof:
walproof rehearse examples/unsafe-plan.json
walproof rehearse examples/safe-plan.jsonThe first exits 1 even though its backup opens. The second uses
sqlite3.Connection.backup
and exits 0.
A rehearsal passes only when every applicable check passes:
| Check | What it proves |
|---|---|
command_exit_zero |
The child process exited normally with code 0. |
backup_exists |
The expected backup artifact was created. |
integrity_ok |
SQLite opened it and PRAGMA quick_check returned ok. |
lab_identity_matches |
The file came from this rehearsal, not a stale artifact. |
baseline_commits_present |
Every row committed before command launch survived. |
event_sequence_contiguous |
Restored fixture IDs contain no gaps. |
event_payloads_valid |
Restored payloads match the deterministic fixture contract. |
JSON and Markdown reports contain the schema/tool version, outcome, safe command metadata, row bounds, and ordered checks. The evidence model never stores literal argv or exception text, and child stdout/stderr is discarded because an arbitrary command may expose credentials there.
Exit codes are stable:
| Code | Meaning |
|---|---|
0 |
The rehearsal passed, or the demo proved both expected paths. |
1 |
The command ran or was attempted, but backup evidence failed. |
2 |
CLI input, the plan, or report output was invalid. |
WalProof fails fast and keeps evidence when --report-dir is set.
| Failure | Repair path |
|---|---|
baseline_commits_present fails while integrity_ok passes |
Your method made a stale snapshot. Replace main-file copy with the SQLite Backup API or another coherent snapshot mechanism, then rerun the same plan. |
lab_identity_matches fails |
The command copied the wrong source or reused an old destination. Make it write exactly {backup} from {source}. |
command_exit_zero reports timed_out |
Run the backup command directly in a trusted environment to diagnose it. Increase timeout_seconds only if the operation legitimately needs more time; the valid range is 1–300 seconds. |
| Exit 2 reports an invalid plan | Remove unknown fields, use an argv array, and include exact {source} and {backup} items. Name an interpreter explicitly instead of a .bat or .cmd launcher. |
| The release gate fails after dependency changes | Run uv sync --locked, fix the first failing gate, and rerun the complete command. Do not skip the audit, isolated install, or demo. |
WalProof discards child output by design. If diagnosis needs that output, execute the reviewed command manually in an environment where displaying it is safe.
- A plan is executable code under your current user account. Review it first. WalProof uses
an argv array and
shell=False, but v0.1 does not sandbox the executable or descendants. - The child inherits the current environment. Put secrets in neither the plan nor diagnostic output. Reports never persist raw command arguments or child output.
- WalProof creates and checks only its own temporary SQLite database. Its public CLI accepts no production database path.
- Passing proves the command handled this synthetic WAL/live-write scenario. It does not prove filesystem crash durability, remote upload completeness, retention, encryption, object-store consistency, or a production restore runbook.
- SQLite WAL is not intended for a database on a network filesystem shared by multiple hosts; see SQLite's WAL limitations.
Python's subprocess guidance likewise recommends passing arguments as a sequence and notes
the platform-specific shell risks; WalProof always launches with shell=False. See the
official subprocess security considerations.
The lockfile is authoritative. Install uv, then run:
uv sync --locked
uv run --no-sync python scripts/check.pyThat single release gate runs Ruff, strict mypy, branch coverage at 90% or higher,
pip-audit, wheel/sdist builds, an isolated wheel install, the installed demo, the example
bundle, and release checksums. CI runs the same command on Ubuntu and Windows with the oldest
and newest supported Python versions.
The project deliberately differs from backup engines and post-hoc integrity checkers. See the dated landscape and differentiation review and the architecture decision.
Read CONTRIBUTING.md before opening a pull request. Report suspected vulnerabilities privately as described in SECURITY.md.
WalProof is released under the MIT License.