Taking a v0 + Supabase prototype to production safely
Prototypes built with v0 against a real Supabase project cross a line quietly: the demo acquires users who are not you. Nothing about the stack changes at that moment — same tables, same keys, same policies or lack of them — but the consequences of the configuration do. Hardening before that line is short work; this page is the checklist, with the SQL to execute each item and the honesty to say what no checklist catches.
The tone here is deliberate: none of this criticises the prototype. v0 composes interfaces quickly and wires them to a working backend, which is exactly what a prototype is for. The gap between prototype posture and production posture is a known, finite list on the database side — and unlike most launch tasks, it can be verified mechanically rather than felt confidently.
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.
Prototype posture, stated plainly
Prototype-era databases share a profile. Tables accumulated feature by feature, some with policies and some without, depending on which tutorial or generated answer each one came from. The anon key and project URL are embedded wherever the frontend needed them. Storage buckets may exist for uploads, possibly public because public was the path of least resistance. Auth may be configured or still pending. None of this is wrong internally; it is simply unaudited.
The production version of the same database differs in one property: every exposed table has a deliberate answer to "who may read this, who may write this, proven by what". Deliberate is the operative word — a restrictive policy copied without understanding counts for less than a simple one you can explain. The checklist below gets you to auditable; understanding arrives item by item.
The ten-minute inventory
One query produces the map: every table in the exposed schema, its RLS state, and its policy count. Empty policy counts and disabled flags are the items to resolve; everything else is confirmation. Run it in the SQL editor and save the output — the after version of this list is your evidence that hardening happened:
Interpretation takes longer than execution. Expect a mixture: tables from features you remember, tables from experiments you do not, and policy counts ranging from zero to several. Nothing in the output requires judgment yet — the goal at this stage is simply that the launch decision references a list rather than a feeling.
SELECT n.nspname AS schema,
c.relname AS table_name,
c.relrowsecurity AS rls_enabled,
(SELECT count(*)
FROM pg_catalog.pg_policies p
WHERE p.schemaname = n.nspname
AND p.tablename = c.relname) AS policy_count
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'p')
AND n.nspname = 'public'
ORDER BY c.relname;Close the standard gaps
For each table lacking protection, apply the standard pattern — enable, force, then policies scoped to real ownership columns, with WITH CHECK on anything that writes. Expect this to break any feature that relied on open tables; that breakage is the inventory of places the frontend assumes more access than users should have, collected in one afternoon instead of discovered over months:
Note the last policy in the example: a feedback table may genuinely accept anonymous submissions, and WITH CHECK (true) states that choice explicitly instead of leaving the table unprotected while pretending otherwise. Explicitly permissive and accidentally open look identical in behaviour and opposite in review — the checklist prefers the former everywhere it applies.
ALTER TABLE public.feedback ENABLE ROW LEVEL SECURITY; ALTER TABLE public.feedback FORCE ROW LEVEL SECURITY; CREATE POLICY "feedback_owner_select" ON public.feedback FOR SELECT TO authenticated USING ((SELECT auth.uid()) = user_id); CREATE POLICY "feedback_anyone_insert" ON public.feedback FOR INSERT TO anon, authenticated WITH CHECK (true);
The rest of the launch list
Beyond tables: check storage buckets, because a public bucket publishes every object in it to whoever holds or guesses URLs, and prototype upload flows rarely revisit that choice. Confirm which operations genuinely need the service_role key and that it lives only in server-side environments — never in the bundle. Then test behaviour rather than configuration: issue requests as an anonymous caller and confirm denials where you expect them.
An honesty note before the product paragraph: RowShield automates much of this list mechanically — catalog audits for RLS state and policy presence, a probe that verifies what the anon key can read on the deployed app — but it does not automate v0-specific analysis, and nothing on this page should be read as implying a check that understands your generated components. The database-side checks are general and real; the interface-side judgment remains yours. RowShield is an independent product, unaffiliated with and not endorsed by Supabase.
Frequently asked
- Will hardening break the prototype right before launch?
- Features that relied on open tables will break, and that is the list you want — every failure names a place the frontend assumed access nobody sanctioned. Schedule the work, apply enable-and-policies together per table, and fix forward through the failures rather than deferring protection to after real users arrive.
- Are public buckets acceptable for prototype images?
- Public means published: anyone with or able to guess object URLs can retrieve the files, and uploaded content has a way of becoming sensitive. For launch, prefer private buckets with signed URLs for anything user-generated, and keep truly public assets — logos, marketing imagery — as the bucket's entire population.
- When is the right moment to do this?
- Before the first user you did not personally recruit, ideally weeks before, because policy changes surface frontend assumptions that need fixing calmly. The inventory query costs ten minutes whenever it runs; running it early converts a launch blocker into a normal task on the list.
Check your project in about ten seconds
Paste a URL. No signup, no writes, nothing stored.
Run the free audit