RowShield
Guides

Policy-as-code habits for Supabase: Terraform and beyond

Policy-as-code is a habit wearing a tool's clothing. Whether your policies travel through Terraform resources, migration SQL or a mixture, the property that prevents drift is the same: the database's security configuration exists in exactly one reviewable place, and changes reach it through no other path.

This page covers where policies practically live on Supabase projects, the Terraform-specific habits that keep definitions honest, why divergence happens anyway even to disciplined teams, and how scheduled scanning functions as the detector that code review structurally cannot be. It is guidance, not automation: RowShield watches the resulting database and claims no role in your repository layout.

RowShield does not detect this yet. This guide gives you the catalog queries to check it yourself. The nine rules that do ship are listed on the rules index.

Where policies actually live

On a Supabase project, infrastructure tools can manage the project's surroundings — settings, some platform resources — while row-level security statements almost always belong with schema migrations, because policies reference columns and depend on tables arriving in order. Treating CREATE POLICY as schema, not infrastructure, keeps it diffable beside the tables it protects.

Whatever the split, the invariant matters more than the boundary: one source of truth per policy. A policy defined twice — once in code, once patched live — is undefined, and the second author of any change decides reality without review.

Habits that hold the line

Four habits carry most of the value. Route every change through review, including what looks trivial — a policy edit is an access-control decision, however small the diff. Review diffs semantically: a renamed variable inside a policy predicate can silently widen what it grants.

Then pair completions: a new table ships with ENABLE and FORCE row-level security and its policy set in the same change, INSERT policies carry WITH CHECK, and nothing merges with a placeholder condition. Finally forbid out-of-band paths explicitly — console edits in production get treated as incidents, because they bypass every habit above at once.

Why divergence happens anyway

Even disciplined teams meet divergence through paths no review covers. Restores roll the database back to a schema from before several migrations. Hotfixes applied under pressure land live first and reach the repository eventually, or never. Colleagues with legitimate access solve problems directly in a session. None of it is malice; all of it is drift.

This is the structural gap: review sees proposed change, while the database experiences actual change. Between the two sit every path listed above, and no amount of process closes the gap completely — which is why detection belongs in the runtime, comparing what exists against what existed.

Scanning as the drift detector

RowShield's role begins where the repository ends. Scheduled scans read the live catalog — pg_catalog and bucket metadata only, never rows — evaluate the nine rules, and diff against the previous run: policies that appeared, came back or disappeared become drift events; posture findings become created, regressed, resolved transitions.

The combination completes the loop policy-as-code opens: code declares intent, review guards the path, and scheduled comparison catches everything that arrived another way. RowShield is an independent product, unaffiliated with and not endorsed by Supabase. See what a scan says about your project now — the free audit runs at rowshield.dev/audit with nothing but a URL.

Frequently asked

Should RLS policies be managed in Terraform or migrations?
Policies usually belong with schema migrations because they reference tables and columns directly; Terraform-style tooling fits the surrounding platform configuration well. The decisive habit is a single source of truth per policy, whichever boundary you draw.
How does RowShield detect drift if our policies live in git?
It compares the live database against its own previous scan, not against your repository. Changes reaching production through console edits, sessions or restores therefore still surface as appeared, came-back or disappeared events.
Is this page about automating Terraform for me?
No — the page is guidance about habits, and RowShield automates neither Terraform nor your repositories. Its automation covers scanning the live database and reporting findings and drift on a schedule.

Check your project in about ten seconds

Paste a URL. No signup, no writes, nothing stored.

Run the free audit
terraform supabase rlspolicy as code postgressupabase terraform policiesinfrastructure as code database security