The two-minute RLS self-check
Five questions. Any "no" is a place nothing would have told you. Each one takes under ten minutes to check by hand.
-
Q1. Have you signed in as a second user and tried to read, change and delete the first user's rows?
Hand check: Open two browsers. Sign in as two different users. From the second, try to read, update and delete the first user's rows. All three should fail. Or in the SQL editor on a staging project:
begin; set local role authenticated; set local request.jwt.claims = '{"sub":"USER_B_ID"}'; select count(*) from public.your_table where user_id = 'USER_A_ID'; -- expect: 0 rollback; -
Q2. Have you read every policy on your most sensitive table, including the oldest one?
Hand check: Postgres combines permissive policies with OR, so one loose policy overrides every careful one. List them all:
select policyname, cmd, roles, qual, with_check from pg_policies where tablename = 'your_table'; -
Q3. Does every INSERT and UPDATE policy have a with check clause that is not true?
Hand check: In the same pg_policies output, read the with_check column for every INSERT and UPDATE row. "true" means anyone the policy covers can write rows under any user_id.
-
Q4. Is your service_role key absent from every client bundle and public env variable?
Hand check: Search your built frontend and any NEXT_PUBLIC_, VITE_ or EXPO_PUBLIC_ variables for the service key. The service_role key bypasses every RLS policy by design. It belongs on a server only.
-
Q5. Could you show a customer, today, evidence of what an outsider can and cannot reach?
Hand check: Write down how you would answer "how do you verify tenant isolation?" If the answer is a dashboard screenshot, you do not have evidence yet.
RowAttest runs checks 1, 2 and 3 across every table on a staging copy of your project, makes real attempts to reach other users' data, and signs what it finds in a report anyone can verify. Free: one project, 10 runs a month.