RLS boundaries
RLS filters rows for a given SQL statement. It does not replace network controls, gateway verification, or correct GRANT configuration.
What you will learn
- GRANT vs policy order of operations
- Failure modes: empty results vs errors
- Why dedicated projects require RLS while pooled projects have another platform boundary
The idea
Postgres evaluates privileges before RLS filters. If the role cannot SELECT the table, you see 42501, not “zero rows”.
On v2, the architecture spec notes RLS is not required initially for the baseline threat model—gateway authentication plus schema and role separation carry the platform isolation story. Adding RLS is an application choice with performance and complexity tradeoffs.
On v1 dedicated, Traefik routes directly to the project's PostgREST container. There is no Flux gateway authentication layer, so an RLS-disabled table that still grants write access to anon, authenticated, or PUBLIC is world-writable. flux push enforces that unrestricted-write invariant transactionally (effective privileges, including inherited and PUBLIC grants). RLS disabled with only reads, and RLS enabled with no policies, are warnings — the latter is secure by default in Postgres but usually unintentional. flux doctor uses the same classification.
How it works
Typical checklist:
GRANTappropriate table/schema privileges to the JWT role.ENABLE ROW LEVEL SECURITY.- Add policies that reference stable claims (
sub, org id, …). - Test with real tokens, not only superuser sessions.
Example
A policy that compares uuid to a text claim silently returns no rows—type discipline matters. The full diagnostic flow for "empty array instead of error" lives in Troubleshooting; the diagnostic for 42501 (when the role cannot reach the table at all, before RLS is even consulted) is at Troubleshooting → 42501.