A tenant_id column with filtering in application code is not isolation, it is a convention that holds until one query forgets. With nine firms you want the database to refuse, not your code to remember.
Put RLS on every table with membership as the condition, not a claim you pass in:
create policy tenant_isolation on invoices for all
using (tenant_id in (select tenant_id from memberships where user_id = auth.uid()))
with check (tenant_id in (select tenant_id from memberships where user_id = auth.uid()));Three things that bite people after this. Index memberships (user_id, tenant_id) or every query pays for the subquery. Views do not inherit RLS unless you create them with security_invoker = true. And any code path using the service role key bypasses all of it, so audit those routes specifically, they are where the real leak will come from.