Repository navigation
Releases
v0.4.4: listed in the official MCP Registry
PgVouch is now listed in the official MCP Registry as io.github.Shubh-Sgr/pgvouch, so MCP clients and directories can find and start it (npx pgvouch mcp, over stdio).
Added
- server.json for the MCP Registry: SOURCE_DATABASE_URL and TARGET_DATABASE_URL required, PGVOUCH_LLM optional (default none).
- package.json names the registry entry (mcpName), which the registry checks against the npm package.
Changed
- README: npm provenance has been on since 0.4.1.
No code changes. Built and signed on GitHub Actions with npm provenance.
v0.4.3: keyword names, shadow with RLS roles and partitions, receipts without personal data
Fixes found by testing with hostile and real-world schema shapes: names that are Postgres keywords, row-level security policies for named roles, partitioned tables, and personal data in receipts.
npm install -g pgvouch@0.4.3
0.4.3
Fixed
Names that are Postgres keywords made generated SQL invalid. Only a short hand-written list of reserved words was quoted, so a table or column named case, window, both, binary, current_date (53 keywords in all) produced ALTER TABLE window ...: plan, shadow and the MCP plan_migration failed with a syntax error, and rewrite produced a script that couldn't run. Every keyword Postgres itself quotes is now quoted (the full list from PostgreSQL 13 to 17), and a test checks that each one gives SQL Postgres' parser accepts.
Shadow runs failed on targets with row-level security policies for named roles. A schema dump has no roles, so copying a target with CREATE POLICY ... TO app_user failed with "role does not exist", and shadow runs (and so LLM plans) couldn't run at all. The roles named by policies on the target and source are now created in the shadow container first, as placeholders without login or privileges.
Shadow runs failed for plans that read a partitioned table, e.g. a view over it ("permission denied"). Partitioned tables and partitions weren't handed to the plan role, because a rule meant only for sequences owned by a column skipped them.
A missing or extra materialized view is described as "materialized view", not "view".
Changed
Receipts no longer contain row values. Differing rows are listed by primary key and changed column names, like the MCP tools: a receipt is meant to be attached to tickets and audits, and row values can be personal data. pgvouch receipt --include-values keeps them.
Full changelog: v0.4.2...v0.4.3
v0.4.2: fix row-level security rollback
Patch release: the suggested rollback of a row-level security step now restores the previous state exactly.
npm install -g pgvouch@0.4.2
0.4.2
Fixed
The rollback of a row-level security step could leave the table forced. Plans set both RLS flags (on/off and forced) even when only one differed, so the suggested rollback flipped the other one too (e.g. turning RLS on, then rolling back, ended with FORCE ROW LEVEL SECURITY). A step now changes only the flags that differ, and its rollback restores the target's previous state exactly.
Full changelog: v0.4.1...v0.4.2
0.4.1: signed releases, ARM Docker image
PgVouch releases are now built and signed on GitHub Actions with npm provenance, and each version is approved by a maintainer with 2FA before it goes live. The Docker image now runs natively on ARM (Apple Silicon, Graviton).
npm install -g pgvouch@0.4.1
npm audit signatures # verifies the provenance
0.4.1
Added
The Docker image is multi-arch (amd64 and arm64), so it runs natively on Apple Silicon and ARM servers instead of under emulation.
Releases are published from GitHub Actions with npm provenance (trusted publishing, no npm token stored): each version on npm links to the commit and workflow run that built it. They are staged first and go live only after a maintainer approves them with 2FA, so CI alone can't publish a version.
README: what PgVouch can touch (network, processes, files), for security reviews and supply-chain scanner reports.
Changed
A test for hostile HTML in PR review output now uses case-insensitive substring checks (CodeQL js/bad-tag-filter); it is stricter than the regex it replaces.
Full changelog: v0.4.0...v0.4.1
v0.4.0: drift beyond tables, sequence check, verify --recheck
PgVouch now checks the objects around your tables (views, functions, triggers, enums, extensions, row-level security), catches sequences left behind after a copy, and verifies data while replication is still running.
npm install -g pgvouch@0.4.0
pgvouch verify --rows --recheck 3 # target still being replicated to: ignore rows in flight
0.4.0
Added
Drift detection for the objects around the tables: views and materialized views, functions and procedures, triggers (including a trigger that is disabled on the target), enum types, extensions, and row-level security (policies and the per-table on/off/forced state). Before, a missing audit trigger or a dropped RLS policy was reported as "no drift". The rules plan fixes them in dependency order (extensions and enums, tables, functions, views, triggers, policies, then RLS on), replaces triggers and policies atomically in one BEGIN ... COMMIT, and holds back drops of extra objects as contract steps. Extensions, changed materialized views and enums with extra labels are left as named manual steps.
Sequence health check in verify: every identity/serial sequence on the target must be ahead of the largest value in its column. A sequence left behind after copying data makes the next INSERT fail with a duplicate key; verify now reports it (exit code 1) with the setval statement that fixes it. Also in the MCP tool verify_data.
Lag tolerance in verify (--recheck , --recheck-delay ), for a target that is still being replicated to. Rows written a moment ago on the source differ on the target only because they are in flight. A recheck looks at the same differences again after a delay, in a new snapshot, and reports only those that never caught up; it rechecks only what the first check found, so new writes don't keep a table failing. Rows still differing whose source row also changed between checks are marked "source still changing". Chunks with too many differences to list, and tables without a primary key, are rechecked by hash. Also on the MCP tools verify_data and find_differing_rows (recheck, recheckDelaySeconds). Tested against real logical replication with live writes: without recheck 7 of 10 runs reported rows in flight as differences; with --recheck 2 --recheck-delay 1, 0 of 10, and the real difference was found every time.
verify --schema <names...> and a schemas input on the MCP tool verify_data (unknown schemas are an error, like diff --schema).
verify notes when row-level security limits which rows the read-only role can see, so "identical" isn't mistaken for "every row compared".
CI runs the integration tests and the lock-accuracy eval on PostgreSQL 13, 14, 15, 16 and 17. The demo databases take PG_VERSION (default 16).
Lock rules for CREATE TRIGGER, ENABLE/DISABLE TRIGGER, DROP TRIGGER, CREATE/ALTER/DROP POLICY, ENABLE/DISABLE/FORCE ROW LEVEL SECURITY, views, materialized views, functions, enum types and extensions, each measured against pg_locks in the lock-accuracy eval.
Eval scenarios 24-32 (views, functions, triggers, enums, extensions, RLS, a sequence left behind its data).
Changed
The LLM planner is off by default (PGVOUCH_LLM=none). Nothing is sent to any model, local or remote, unless you set PGVOUCH_LLM=ollama or gemini.
Shadow runs use a Postgres image of the target's major version (a newer server can't be dumped by an older pg_dump), and the plan role also owns copied functions and types, so plans can replace them.
Drops of extra objects that aren't data (views, functions, triggers, policies, enum types, sequences) are contract-phase steps now, commented out unless --allow-data-loss.
Fixed
Node 21.0–21.6 passed the engines check but couldn't run the CLI (process.loadEnvFile arrived in 21.7). engines is now ^20.12.0 || >=21.7.0.
Text primary keys could give false mismatches between servers with different collations (another collation, or the same one on a different glibc/ICU version, common in cloud moves). Key ordering and chunk ranges now use byte order (COLLATE "C") for text-like keys; integer, uuid and timestamp keys are unchanged.
Full changelog: v0.3.2...v0.4.0
v0.3.2: now on npm
PgVouch is now on npm.
npx pgvouch locks my_migration.sql --offline # try it now, no database needed
npm install -g pgvouch0.3.2
Changed
- README: a "Get started" section with two clear paths. Option A uses the npm package: try it on a migration file with no database, install, point it at your databases, then the full command list as
pgvouch <command>. Option B clones the repo for the seeded demo databases and the step-by-step walkthrough. No code changes.
0.3.1
Added
- Published on npm. Install with
npm install -g pgvouch, or run any command withnpx pgvouch <command>. MCP clients can start the server withnpx -y pgvouch mcp, so registering it no longer needs a clone and a build. - The installed CLI reads
./.env, likenpm run clidoes. Variables already set in the environment take precedence. The MCP server still takes its settings only from the MCP client config.
Full changelog: v0.3.0...v0.3.2
v0.3.0: renamed to PgVouch, Apache 2.0
Changed
Renamed from DriftGuard to PgVouch (the npm name driftguard belongs to an unrelated project). Breaking for existing setups: the CLI is now pgvouch, environment variables are PGVOUCH_* (was DRIFTGUARD_*), the demo read-only role is pgvouch_ro, the MCP server name is pgvouch, and the Docker image is ghcr.io//pgvouch. Recreate the demo databases with npm run db:down && npm run db:up.
License changed from MIT to Apache 2.0, which adds an explicit patent grant from contributors. A NOTICE file ships with the npm package and the Docker image.
Fixed
Type-change rewrite kept indexes and constraints on the old column. After the expand/contract swap, a foreign key, CHECK, UNIQUE constraint or index on the column stayed on _old, so the rules plan failed its own shadow run (e.g. transactions_merchant_id_fkey ended up on merchant_id_old). They are now copied to the new column without blocking (index CONCURRENTLY, FK/CHECK NOT VALID + VALIDATE, UNIQUE via a concurrent index + USING INDEX) and take their original names in the swap. A primary key, exclusion constraints and foreign keys from other tables stay a named manual step. Two new eval scenarios cover it (23 in total, 19/19 rules plans pass the shadow run).
The column swap is atomic. The two renames now run inside one BEGIN ... COMMIT, so there is no moment where the column name doesn't exist.
The plan validator accepted any DROP. DROP SCHEMA ... CASCADE, DROP VIEW, DROP FUNCTION etc. passed and were rendered as normal steps. Only DROP INDEX and DROP TABLE are allowed now.
Typos gave misleading answers. An unknown schema (diff --schema typo) reported "No schema drift"; an unknown table (verify --table typo, MCP find_differing_rows) reported "DIFFERENCES FOUND". Both are now errors. --chunk-size and --max-rows reject invalid numbers up front, and receipt-verify says clearly when a file isn't a receipt.
DriftGuard v0.1.0
First public release of DriftGuard: an MCP server + CLI that lets AI assistants safely inspect, plan and prove PostgreSQL migrations. The LLM proposes; deterministic code decides.
What's included
- Schema drift detection (pg_catalog introspection + pure diff with severities)
- Data verification with chunked checksums, and bisection down to the exact differing rows
- Lock-impact analyzer built on PostgreSQL's own parser (libpg-query)
- Safe-rewrite engine: CONCURRENTLY, NOT VALID + VALIDATE, expand/contract, batched backfills
- Guarded LLM planner (Ollama / Gemini) with a validator and rules-only fallback
- Shadow runs in a disposable Postgres container, reversibility tags + rollback SQL, hashed receipts
- MCP server with 6 read-only tools for Claude Code, Cursor and other MCP clients
Measured results (see evals/)
- Drift detection: 100% precision / 100% recall on 21 seeded scenarios
- Bisection found every seeded row change reading 311 of 6,079,997 rows
- Lock modes: 30/30 predictions match
pg_locks; table rewrites: 29/29 matchrelfilenode - App stall on a 1M-row table: CREATE INDEX 901 ms -> 37 ms, ALTER COLUMN TYPE 8.9 s -> 174 ms
- Rules-only plans verified by shadow runs: 17/17
Get started
See the Quickstart and the MCP setup guide.