Lomi is a SaaS for dog kennels in Colombia, and it has two kinds of tenancy at once: each kennel is a tenant with private data, but there is also a public show-dog community where some of that data is shared. Getting that right with Postgres Row Level Security taught me more about RLS than any tutorial. Here is what I learned shipping it, including the bug that would have leaked one kennel's data to another.
Why RLS instead of filtering in the app
I could have written WHERE kennel_id = $current on every query and called it multi-tenant. The problem is that one forgotten WHERE clause leaks everything. The security boundary lives in a hundred query sites, and all hundred have to be perfect forever.
RLS moves the boundary into the database. The policy is enforced no matter which query, which endpoint, or which junior developer wrote the code. You cannot accidentally select another tenant's rows, because Postgres filters them out before your query ever sees them. That is the right place for a security boundary: as close to the data as possible, enforced once.
The basic single-tenant policy






