anon key, so the only thing standing between an end user and the firstflow_* tables is Row Level Security (RLS). This page explains the model the migrations ship with, the two access paths, and how to tighten policies for production.
Two access paths
The
service_role key bypasses RLS by design it is trusted backend code. Keep it server-only (never NEXT_PUBLIC_*, never committed). All untrusted, browser-originated writes go through the anon key and are subject to your policies.
What the migrations ship
0001_initial.sql creates the tables and 0002_strict_rls.sql enables RLS on them. The shipped policies are a baseline; because FirstFlow trusts whatever user.id your app provides (it has no auth of its own), the SDK cannot know how your users map to rows. Treat the shipped policies as a starting point and adapt them to your auth model.
Read
0002_strict_rls.sql in the repo before relying on the defaults, and
confirm the policies match how your app authenticates to Supabase (a Supabase
Auth session, a signed JWT, or anon-only).Tightening policies
The right policy depends on how your browser client authenticates to Supabase:-
You use Supabase Auth in your app. The signed-in user’s JWT carries
auth.uid(). Scope rows to it so users only touch their own: -
You do not use Supabase Auth (the browser holds only the
anonkey). The database cannot verify theuser_ida client sends, so anon writes are effectively trust-on-write. For stronger guarantees, proxy persistence through your own backend with theservice_rolekey, or mint a short-lived signed token (seesignUserTokenin the Server SDK) and verify it in a policy.