1.0.1-alpha
Released 2026-08-31. Testate is a self-hosted tool for QA teams. It takes data-only snapshots of the databases behind a system under test, restores them on demand, diffs them, imports fixtures, and lets an AI agent read them. One container, one volume, on your own network. Overview at https://pt-perkasa-pilar-utama.github.io/testate/, full record in the changelog.
This is the first usable release. 1.0.0-alpha was published and cannot boot: docker-slim removed /data from the image, so the first run against an empty volume refused with TESTATE_DATA_DIR is not writable. Use this version instead.
Install
docker run -d --name testate -p 3000:3000 -v testate-data:/data \
-e TESTATE_SECRETS_ACTIVE_KEY="$(openssl rand -base64 32)" \
-e TESTATE_ADMIN_PASSWORD=change-me-now-1234 \
ghcr.io/pt-perkasa-pilar-utama/testate:1.0.1-alphaFor a deployment you keep, use Compose and an .env file: How to install. To run from source instead, copy apps/api/.env.example to apps/api/.env and use bun run dev.
Quick start
Open http://localhost:3000 and sign in as admin with the password you set. Testate makes you change it on the first login.
The API answers as soon as the container is up:
$ curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/api/v1/health/live
204
$ curl -s -X POST http://localhost:3000/api/v1/auth/login \
-H 'content-type: application/json' -H 'X-Testate-Request: 1' \
-d '{"username":"admin","password":"change-me-now-1234"}'
{"data":{"user":{"id":"01a05603-8398-7668-975f-69b40b380d6a","username":"admin","display_name":"admin","role":"admin"},"must_change_password":true}}Then add a database under Adapters, snapshot it under States, break something, and put it back under Checkouts. Where to point the adapter's host is the one thing that catches people out, so read Connecting to a database first.
What it does
- Snapshots that hold data, not schema: one state covers every database in a project, and a checkout restores them and reports what happened per database.
- Diffs you can read: compare two states, or a state against the live database. The dialog leads with the totals and hides the tables that did not move, so a change in one table out of two hundred is the first thing you see.
- A grid that behaves like a database client: filter, sort, page by keyset, follow a foreign key into the next table. Write mode stashes the table before the first write.
- Fixtures with a dry run: upload a CSV or XLSX, map the columns, dry-run it, then import. Rejected rows come back as a file you can fix and re-import.
- Read-only agent access over MCP: point Claude or any MCP client at
/api/v1/mcpwith an agent token. It getslist_tables,describe_table,page_rows,run_readonly_query,extract_fixtureand more, capped at 1000 rows and 15 seconds a query. There is no write tool, and column policies mask values before the agent sees them. - An agent that arrives knowing how to use it: the first tool in
tools/listishelpand the first resource istestate://guide, both serving the same document: how the pieces relate, the order to call things in, and the limits that refuse a call. Every tool carries its own description, so an agent reading the list knows what each one answers. - A REST API under every screen:
POST /projects/{slug}/checkoutsresets a database before a test run. The contract is served at/api/v1/openapi.jsonby a running instance.
Engines
| Tier | Engines | What you get |
|---|---|---|
| Tabular | PostgreSQL, MySQL, MariaDB | view, snapshot, checkout, diff, extract, edit, import |
| Document | MongoDB | view, snapshot, checkout, diff, extract |
| Files | S3, SFTP, FTP | view, download |
| REST | any HTTP API | saved requests, hooks around checkouts |
Every engine in that table is real. None is a stand-in, and each has a contract suite that runs against the actual server rather than a mock.
Known limits
Read What it does not do before you install this. The three that bite hardest:
- Databases go one at a time: even inside one project, Testate snapshots and restores them in sequence. Each is correct alone, the set is not guaranteed to line up. Keep the app idle while it runs.
- It only resets what you add: an untracked database survives a reset and Testate cannot warn you, because it has never heard of it.
- Microservices are untested: every limit above compounds. Stay inside one service, and add the rest as read-only adapters.
It also ships a one-line patch to @solidjs/signals, because a stale transaction stamp made the scheduler spin forever on the data grid. The evidence is in patches/ and the same fix is upstream as solidjs/solid#3143. It goes away when a Solid release carries it.
Credits
Built on Bun, Hono (MIT), SolidJS (MIT), valibot (MIT), Cloudflare Kumo (MIT), ssh2 (MIT), basic-ftp (MIT), and the MongoDB Node driver (Apache-2.0). The interface follows GitHub's design language and uses Mona Sans (OFL-1.1). Images are slimmed with SlimToolkit.
Testate is MIT licensed.