How to Use the diff Command Modes
One command for every step: report, review, rehearse and apply.
npm install -g @akal/supaforge. A hosted dashboard is on the way - to hear when it opens.Overview
supaforge diff compares a source environment with a target and reports everything that differs. Flags decide how far it goes:
- No flags - a summary: which checks drifted, and how badly.
- --detail - every finding, with the SQL that fixes it.
- --apply - runs the fixes against the target.
- --apply --dry-run - prints the fixes in the order they would run, and runs nothing.
- --apply --prove - rehearses the fixes on a throwaway copy of the target first.
Nothing is written to the target without --apply.
Mode 1 - Summary (Default)
supaforge diffReal output for a staging project one migration ahead of production:
SupaForge scan complete: 4 drift issues found across 3 checks.
5 checks were skipped - coverage is partial.
Source: staging → Target: production
● Layer 1 (Schema): 2 issues [WARNING] (+1 reported under RLS Policies)
● Layer 2 (RLS Policies): 1 issue [CRITICAL]
✓ Layer 3 (RLS Coverage): 0 issues
○ Layer 4 (Edge Functions): skipped - no projectRef or accessToken configured
✓ Layer 5 (Storage): 0 issues
○ Layer 6 (Auth Config): skipped - no apiUrl/projectRef or accessToken configured
● Layer 7 (Cron Jobs): 1 issue [WARNING]
○ Layer 8 (Reference Data): skipped - no tables configured in checks.data.tables
✓ Layer 9 (Webhooks): 0 issues
...
✓ Layer 14 (Postgres Roles & Grants): 0 issues
○ Layer 15 (Queues): skipped - pgmq extension not installed
○ Layer 16 (Partition Sets): skipped - pg_partman extension not installed
Drift score: 70/100 (11 of 16 checks compared)
Posture score: 100/100 (target only - RLS coverage, migration history)
→ Run with --detail to see SQL diffs
→ Run with --apply to fix driftA check that can't run - here, the ones that need a Supabase access token - says so and why. It is never shown as a clean pass. The drift score is 100 when the two environments match.
The command exits 1 when it finds critical drift, so it works as a gate as it is. For pipelines, --ci adds GitHub annotations, JSON on stdout and distinct exit codes: 0 clean, 1 drift, 2 error.
Mode 2 - Detail (--detail)
supaforge diff --detail── Layer 1: Schema ────────────────────────────────────────
⚠ [WARNING] Index missing: public.orders_user_id_idx
SQL fix (UP):
CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id);
── Layer 2: RLS Policies ──────────────────────────────────
✖ [CRITICAL] Missing RLS policy: Users can read own profile
Policy "Users can read own profile" on public.profiles exists in source
but is missing from target. This is a CVE-2025-48757 risk pattern.
SQL fix (UP):
CREATE POLICY "Users can read own profile"
ON "public"."profiles"
AS PERMISSIVE
FOR SELECT
TO "authenticated"
USING ((auth.uid() = id))
;Combine --detail with --check to focus on one check:
supaforge diff --check=rls --detailMode 3 - Apply (--apply)
Before applying, see the exact plan in the order it will run:
supaforge diff --apply --dry-runWould apply 4 fix(es), in this order:
1. [schema] schema-alter-1
ALTER TABLE "profiles" ADD COLUMN "avatar_url" text;
2. [schema] schema-create-index-2
CREATE INDEX orders_user_id_idx ON public.orders USING btree (user_id);
3. [cron] cron-missing-nightly-cleanup
SELECT cron.schedule('nightly-cleanup', '0 3 * * *', $$DELETE FROM public.orders WHERE total = 0$$);
4. [rls] rls-missing-public.profiles.Users can read own profile
CREATE POLICY "Users can read own profile" ON "public"."profiles" ...
Nothing was executed. Drop --dry-run to apply.Then apply. With --prove, SupaForge first replays the fixes on a throwaway copy of the target and applies nothing unless the result matches the source - or if the rehearsal can't run at all:
supaforge diff --apply --prove Proving convergence on a throwaway clone…
Not proved - act outside the proved schemas or on the whole server: cron ×1
Converged - the migration reproduces the source exactly.
Applied 4 fix(es):
✓ [schema] schema-alter-1
✓ [schema] schema-create-index-2
✓ [cron] cron-missing-nightly-cleanup
✓ [rls] rls-missing-public.profiles.Users can read own profileThe fixes run in dependency order and in one transaction: if one fails, none of them stay. Anything that would lose data - dropping a table or column, deleting rows, removing a policy - is reported but held back until you add --allow-destructive. Running the same apply again finds nothing left to do.
The rehearsal copies a sample of the target's rows as well. A fix that would lose them, or change their values, fails the proof, and tables the fixes would rewrite are named. Each finding also says what its fix does on a live database: whether it blocks reads or writes, rewrites the table, or scans every row.
To apply one check only:
supaforge diff --check=rls --applyOr pick individual findings with --only, using the ids from --json (globs allowed):
supaforge diff --apply --only='schema-create-*'One Instant per Database
A scan reads each database as of a single moment, so a migration or change made while it runs is seen by every check or by none, and the report never describes a database that did not exist. As with pg_dump, what the scan reads stays locked until it ends; --no-snapshot reads each query on its own instead.
Schema Files as the Source
If your schema lives in SQL files, such as supabase/schemas, compare the directory with an environment instead of a second database. SupaForge loads the files into a throwaway database on the target's server, compares them and drops it again. The files can be in any order:
supaforge diff --source-dir supabase/schemas --target=productionEvery other flag works the same, including --apply and --prove.
supaforge sync - Shorthand for --apply
supaforge sync is supaforge diff --apply. It takes the same flags, including --check, --skip, --source, --target, --dry-run and --prove:
supaforge sync --source=staging --target=productionsupaforge hukam - An Alias for diff
supaforge hukam works exactly like supaforge diff, with the same flags and output:
supaforge hukam --detailHukam (ਹੁਕਮ) is a Punjabi word meaning "command" or "order" - a nod to the Sikh tradition of taking a daily hukamnama. It's hidden from --help.
Choosing the Right Mode
- A quick look, or a CI gate -
supaforge diff, or--ciin a pipeline. - Before changing anything -
--detailto read the SQL,--apply --dry-runto see the order. - Applying to production -
--apply --prove. - A focused change -
--check,--skip,--tablesor--only.
After each run a dim tip: line suggests the most useful next step for what was found.
Install
npm install -g @akal/supaforgeSummary
One command covers the whole loop: supaforge diff to see what drifted, --detail to read the fixes, --dry-run to see their order, and --apply --prove to make the target match the source safely.
Ready to try SupaForge?
Install the CLI and run your first diff in a minute. Nothing is changed until you add --apply.
npm install -g @akal/supaforge