You have backups.
But when did you last prove they restore?
probavi is a self-hosted, engine-agnostic platform that continuously proves database backups are restorable, and records each proof as signed, auditable evidence with measured restore times.
It takes no backups of its own. Your existing tools already do that well.
“Backup completed successfully” proves almost nothing
Section titled ““Backup completed successfully” proves almost nothing”Backups fail quietly. Corruption in transit. A missing WAL or binlog segment. A version incompatibility that only shows itself during a restore. An encryption key nobody kept. Months of dutifully backing up the wrong database.
Every one of these passes the backup job and fails the restore, and the failure surfaces at the one moment you cannot afford it. Meanwhile “we have backups” is accepted as an answer. The question it answers is not the one that matters: when did you last restore one?
One drill, start to finish
Section titled “One drill, start to finish”A drill is the unit of work — one scheduled restore-verification of one database.
- 1ProvisionA disposable, isolated runtime: a container, a Kubernetes Job, or a bare host over SSH.
- 2RestoreAn adapter performs a real restore of a real backup, driving the engine’s own tooling.
- 3CheckValidations run against the restored database, from “did it come up?” to your own SQL.
- 4RecordOne signed record is appended to the evidence log, and the sandbox is destroyed.
The core knows nothing about pg_dump, WAL segments or binlogs. Every
engine-specific step lives in an adapter — an ordinary external process
speaking a documented protocol, which anyone can implement in any language.
Teardown is forced, including on failure paths. A sandbox holds production data, so nothing about its destruction is left to the happy path.
Runs without you
Section titled “Runs without you”Nothing to install alongside it, nothing to keep alive between drills, and no agent on a database host.
- cron or a systemd timerWhatever already runs on your machines. Probavi ships no scheduler and no daemon of its own.
probavi runOne drill, under a lock file and a timeout. Teardown is forced, including on failure paths.- What it leaves behind
- One signed evidence record
- A Prometheus textfile
- A signed webhook, if you want one
Exit code
- 0
- backup proven restorable
- 1
- recoverability failure (backup, restore, or check)
- 2
- infrastructure error or cancelled
- 3
- usage or setup error
- 5
- evidence record could not be written
This is what makes continuously more than an adverb. A restore you run when you remember to is an exercise; one that runs whether or not anyone remembers is a record — and a history of recoverability is worth exactly as many drills as happened without being asked for.
The exit code is the contract, and it separates the two failures that matter. A backup that did not restore is not the same event as a container runtime that was down, and an alerting rule that cannot tell them apart gets muted inside a month. Both are recorded either way: an error is a drill outcome, not a missing line in the log.
A whole game-day runs the same way. probavi gameday takes member drills in
dependency order and leaves each one its own signed record — the annual
afternoon with a runbook, expressed as one more line in a timer.
The product is the evidence, not the test
Section titled “The product is the evidence, not the test”Running a restore test is a copyable feature. A continuously maintained, cryptographically verifiable history of recoverability is not.
{ "adapter": { "digest": "sha256:4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c", "name": "postgres", "protocol": "probavi-adapter/0", "version": "0.1.0" }, "backup": { "checksum": "sha256:9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f9f", "created_at": "2026-07-30T01:58:02.000Z", "kind": "pgdump", "size_bytes": 565248 }, "checks": [ { "detail": "accepting connections", "name": "service_healthy", "ok": true }, { "detail": "100000 rows (min 100000)", "name": "row_count:orders", "ok": true } ], "drill": { "config_hash": "sha256:7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d", "name": "prod-orders-db", "pitr_target": "2026-07-30T14:32:00.000Z" }, "env": { "arch": "amd64", "host_id": "3f7a9c2e5b1d8e04", "os": "linux", "probavi_digest": "sha256:1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d", "probavi_version": "0.1.0" }, "error": null, "outcome": "pass", "prev_hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "sandbox": { "params": { "image": "postgres:16", "memory": "2GiB" }, "provider": "docker" }, "schema": "probavi-evidence/2", "seq": 1, "sig": { "alg": "ed25519", "key_id": "56475aa75463474c", "sig_b64": "/f2ahJgj08qDVSvbTIZ/+XI7IfkCavp0WfS7ETIezC+E2PMzFzEmX/G8d4d+S42Q9ZegQDU8aMKJTBb/gS5KDQ==" }, "timings_ms": { "engine_ready": 1166, "provision": 1170, "restore": 190, "total": 2840, "transfer": 110, "validate": 61 }, "ts": "2026-07-31T02:00:11.482Z"}A real record from the format’s published example log, re-indented for reading — on disk it is one canonical line, and that is the form the signature covers.
Signed with:03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8
Records are chained: each carries the SHA-256 of the one before it, and each is signed with ed25519. The log is append-only — no code path in Probavi rewrites or deletes a record that already exists.
- each record carries the previous record’s hash
- alter one record and every link after it stops verifying
Checking all of it takes the log file and a public key. No network, no licence, and not even a Probavi installation, since the format ships with an independently released verifier. Verification is never paywalled. For a product whose output is proof, that is not a pricing decision.
Engine-agnostic, and why that holds
Section titled “Engine-agnostic, and why that holds”Two pluggable axes either side of a core that knows nothing about databases or container runtimes.
What ships today
Section titled “What ships today”- 24engine adapters
- 3sandbox providers
- 5check kinds
- 8CLI commands
- 24CLI languages
- 4versioned specifications
Engines. Verified against means what it says: the versions this project’s integration suite restores from on every run. It is not a supported-version range, and the project makes no such claim.
| Engine | Verified against | Sources | Point-in-time recovery |
|---|---|---|---|
| Apache Cassandra | 4.1, 5.0 | cassandra_snapshot cassandra_snapshot_dir cassandra_snapshot_tar | — |
| ClickHouse | 26.3, 26.7 | clickhouse_backup clickhouse_backup_dir | — |
| Apache CouchDB | 3.5.2, 3.4.3 | couchbackup couchbackup_dir couchdb_data couchdb_data_tar | — |
| DuckDB | 1.4, 1.5 | duckdb_db duckdb_db_dir duckdb_export | — |
| Elasticsearch | 8.19.20, 9.5.2 | elasticsearch_repo elasticsearch_repo_zip | — |
| etcd | 3.5, 3.6, 3.7 | etcd_snapshot etcd_snapshot_dir | — |
| Firebird | 5.0.4, 4.0.7 | firebird_gbak firebird_gbak_dir | — |
| H2 | 2.4.240, 2.3.232 | h2_backup h2_backup_dir h2_db h2_db_dir | — |
| InfluxDB | 2.7, 2.8, 2.9 | influx_backup influx_backup_dir influx_backup_tar | — |
| MariaDB | 10.11, 11.4, 11.8, 12.3 | mariadb_backup mariadb_dump mariadb_dump_dir | — |
| MongoDB | 7.0, 8.0 | mongodump mongodump_dir mongodump_with_oplog mongodump_with_users | — |
| Microsoft SQL Server | 2019, 2022, 2025 | bak bak_chain bak_dir bak_with_logins | — |
| MySQL | 8.4, 9.7Variant image: percona/percona-server:8.4.10 | mysqldump mysqldump_dir mysqldump_with_users xtrabackup | — |
| Neo4j | 5.26 | neo4j_dump neo4j_dump_dir | — |
| OpenSearch | 2.19.6, 3.8.0 | opensearch_repo opensearch_repo_tar | — |
| Oracle Database | 23.26.3.0 | oracle_datapump | — |
| PostgreSQL | 14, 15, 16, 17, 18Variant image: pgvector/pgvector:0.8.6-pg17Variant image: timescale/timescaledb:2.29.1-pg17Variant image: postgis/postgis:17-3.5 | pgbackrest pgdump pgdump_dir pgdump_with_globals timescaledb_dump timescaledb_dump_dir | Yes |
| Prometheus | 3.13 | prometheus_snapshot prometheus_snapshot_dir prometheus_snapshot_tar | — |
| Qdrant | 1.19.0, 1.18.1 | qdrant_full_snapshot qdrant_full_snapshot_dir qdrant_snapshot qdrant_snapshot_dir | — |
| Redis | 7.2, 7.4, 8.2, 8.10 | redis_aof redis_rdb redis_rdb_dir | — |
| Apache Solr | 10 | solr_backup solr_backup_dir solr_backup_tar | — |
| SQLite | 3.46, 3.49, 3.50, 3.51, 3.53 | sqlite_db sqlite_db_dir sqlite_dump sqlite_dump_dir | — |
| Valkey | 7.2, 8.0, 8.1, 9.0, 9.1 | valkey_aof valkey_rdb valkey_rdb_dir | — |
| VictoriaMetrics | 1.150 | victoriametrics_backup victoriametrics_backup_dir victoriametrics_backup_tar | — |
Sandboxes. Where the restore happens, and what is guaranteed about it.
docker— Docker no published portsforced teardownk8s— Kubernetes Job no published portsforced teardownremotehost— Bare host over SSH no published portsforced teardown
Checks. What a drill can assert about the restored database.
service_healthy— Engine answers the adapter's healthchecktable_exists— Table exists and is queryablerow_count— Row count within boundsfreshness— Newest row younger than a maximum agesql— Custom SQL assertion (your own statement)
Commands. The whole surface of the binary.
probavi runprobavi gamedayprobavi pushprobavi evidence verifyprobavi evidence keygenprobavi adapter probeprobavi adapter conformanceprobavi version
Specifications. Versioned independently of the binary, so an integration can target a contract rather than a release.
- Adapter protocol —
probavi-adapter/0 - Evidence schema —
probavi-evidence/2(the verifier also readsprobavi-evidence/0probavi-evidence/1) - Notification payload —
probavi-notification/1 - Evidence push —
probavi-evidence-push/1
What Probavi is not
Section titled “What Probavi is not”The boundaries are part of the offer, so they are stated rather than implied.
- Probavi takes no backups. It verifies backups produced by other tools — pgBackRest, wal-g, Barman, mysqldump and their equivalents — which are its foundation, never its competitors.
- Probavi ships no scheduler and no daemon of its own. Drills are started by cron or a systemd timer, with a lock file and a timeout.
- Probavi runs no agent or daemon on database hosts.
- Probavi manages no secrets. It reads credentials from environment variables or files for the duration of one drill, and redacts them from logs and evidence.
- Probavi has no telemetry and never phones home. It is a trust product; that is not negotiable.
- Probavi ships no web interface. The shipped surfaces are the command line, a Prometheus textfile, and webhooks.
It stands on your backup tools, not against them
Section titled “It stands on your backup tools, not against them”That is also why this is worth self-hosting. Managed restore testing exists inside single cloud ecosystems, and enterprise backup suites verify at the virtual-machine level. Neither helps a mixed estate of databases on your own machines.
Recovery testing you can show someone
Section titled “Recovery testing you can show someone”DORA, NIS2 and NIST’s contingency-planning guidance all expect recovery capability to be tested and documented, not merely configured. Automated drills produce exactly that: dated, signed records of what was restored, how long it took, and what was verified about the result.
Open core, and what stays that way
Section titled “Open core, and what stays that way”The complete verification loop is open source under Apache-2.0 and stays that way: drills and game-days, adapters, sandbox providers, checks, evidence generation, evidence verification, the command line, the metrics. Anyone can prove their backups restore, indefinitely, without asking permission. The specifications and the independent verifier are public for the same reason — evidence that only its producer can check is not evidence.
Nothing in the binary is unlocked by a network call. Any licence key it ever checks is an ed25519-signed token, verified offline — a product that promises never to phone home cannot make an exception for its own billing.
Getting started
Section titled “Getting started”A drill is a file:
target: name: prod-orders-db adapter: postgres source: kind: pgdump path: /backups/orders/latest.dumpsandbox: provider: docker params: image: postgres:16 timeout: 30mchecks: - builtin: service_healthy - builtin: row_count table: orders min: 100000evidence: path: /var/lib/probavi/evidence.jsonl sign_key: /etc/probavi/ed25519.keyThen one command per drill, and one to check the log:
$ probavi run --config drill.yaml{"outcome":"pass","seq":42,"evidence_path":"/var/lib/probavi/evidence.jsonl","checks_passed":3,"checks_total":3,"restore_ms":252400,"total_ms":318900}
$ probavi evidence verify --log /var/lib/probavi/evidence.jsonl --key probavi.key.pub{"status":"VALID","records":42,"damaged_lines":[],"failed_line":0,"reason":"","head":{"seq":42,"hash":"sha256:1a8f736d2f4350385693385d203799f88721db4f4fd5056d6ceb028088127268"}}The repository has the quickstart, the specifications, and the manifest of everything above.