Blog
Notes on keeping a database honest
Writing from RowShield on row-level security, schema drift, and what it takes to keep a browser-reachable Supabase backend behaving the way you think it does.
What RLS costs: performance and how to index for it
RLS policies run as predicates on every query. Here is how Postgres evaluates them, why (select auth.uid()) matters, and which indexes keep policies fast.
13 min readNew table, no policy: the default-open window
A Supabase table without RLS is readable by anyone holding your public key, on every API surface. What the window exposes, and how to close it structurally.
12 min readGRANTs versus RLS: two permission systems, one database
Table privileges and row policies stack rather than compete - both gates must pass, they fail differently, and these tests prove how your tables behave.
12 min readFrom weekend prototype to production: hardening a Supabase app
The ordered checklist from working prototype to defensible product - RLS steps in full SQL, storage and keys contained, isolation proven before launch.
13 min readMigrations that silently weaken policies
Recreates, CASCADEs, splits and restores weaken Supabase RLS without errors. Four recurring migration patterns in verified SQL, plus a pre/post checklist.
13 min readWhat Supabase gives you, and what remains yours
A fair map of Supabase security features versus your responsibilities - what the platform guarantees, what remains operator work, and where incidents begin.
12 min read"I fixed this three weeks ago"
Findings recur because migrations recreate objects, generators regenerate schemas, and a fix applied in the dashboard is not a fix recorded in the repository.
driftmigrations4 min readReviewing the schema you didn't write
A method for auditing AI-generated or inherited Supabase schemas: read the catalog first, distrust four shapes, check indirect surfaces, then test behavior.
13 min readSchema drift, defined
Schema drift is the accumulation of structural change after the point at which someone last checked the security consequences. Normal, constant, and invisible without comparison.
driftschema4 min readMapping a project's public surface from outside
Supabase generates an endpoint for every table, view and function in your exposed schema. We enumerate one project's entire surface with nothing but a URL and a public key.
postgrestapi4 min readWITH CHECK is the other half
USING governs what a request can read. WITH CHECK governs what it can write. A policy with only the first half lets a user insert rows they will never be allowed to see.
rlspolicies5 min readRLS versus application-layer authorization
Database-enforced and app-enforced authorization fail differently - where each belongs in a Supabase product, and why layering beats choosing one.
12 min readFour policies that say nothing
A policy whose condition is always true satisfies every tool that checks whether a policy exists. Four ways to write one, only the first of which is obvious.
rlspolicies5 min readIs the Supabase anon key safe to expose?
The anon key is meant to be in your frontend. It is safe exactly to the degree your policies are correct, and meaningless as a security boundary on its own.
supabasekeys4 min readThe authorization posture of vibe-coded apps
What AI assistance systematically gets right and wrong about Supabase authorization - observed failure patterns, with an ordered hardening sequence.
12 min readReading a policy is not testing a policy
A policy's text tells you what its author intended. Only a request tells you what the database returns. Here is a procedure for asking, with three identities, one endpoint and a control for every result.
rlstesting7 min readThree bugs that look like one bug
RLS disabled, RLS enabled with no policies, and a policy that always evaluates to true are three different failures with three different fixes. Most advice treats them as one.
rlssupabase6 min readYour AI wrote the schema. Nobody wrote the policies.
We generated a Supabase backend from a prompt, then asked it as a stranger for every row in every table. Here is what came back, and the query that would have warned us.
rlssupabase5 min readThe tenant-isolation test: five queries that prove your boundaries
Five probes to audit your own Supabase project: anon reads, forged writes, cross-tenant access - each with its expected result and failure meaning.
12 min readRLS policies don't fail, they drift: the definitive guide
Watch a correct Supabase authorization model erode through six months of ordinary changes — runnable SQL at every step, no errors thrown, and what catches each.
14 min readHow Supabase authorization actually flows
From sign-in to row visibility: how a JWT becomes a database role, what auth.uid() reads, and where the authorization chain breaks in practice.
12 min readAuditing a Supabase project in one afternoon
The complete manual audit of a Supabase project: catalog queries for tables, policies, grants and keys, how to read each result, and probes that prove findings.
12 min readRow-Level Security in Postgres: the complete evaluation model
How Postgres actually combines policies: permissive OR, restrictive AND, USING vs WITH CHECK per command, who bypasses RLS, and what a denied row does.
13 min readRLS policies do not fail. They drift.
A row-level security policy that was correct when you wrote it can stop covering what it was written for, without erroring, without changing, and without anyone noticing.
supabaserls5 min read