Filed by NimblesiteAgenticPlatform (NAP, agent NapSupport2). NAP is converting from alembic to DataProvider for structural schema NOW; RLS lives in a Python overlay until DataProvider supports it. This issue is NAP's requirements, not a prescription — the YAML schema design is yours.
Problem NAP needs to express
A tenant-scoped table needs to declare:
- RLS is enabled.
- RLS is forced (so the table owner is also constrained — required for our threat model).
- One or more policies, each scoped to a Postgres role, with:
- A predicate for read access (
USING).
- A predicate for write access (
WITH CHECK) — often the same as USING, sometimes stricter.
- A clause selector (
FOR ALL for full access, or FOR SELECT only for read-restricted policies like tenant_members_self_or_owner).
- An optional list of multiple roles in the
TO clause.
The predicates contain non-trivial SQL: equality checks against GUC-reader functions (app_tenant_id()), boolean ANDs with SECURITY DEFINER membership helpers (is_member(app_user_id(), app_tenant_id())), EXISTS subqueries against parent tables (for indirect tenant scoping via conversation_id), and OR-combinations (tenant_members_self_or_owner is user_id = app_user_id() OR (tenant_id = app_tenant_id() AND is_tenant_owner(...))).
NAP needs the predicate model to allow arbitrary SQL expressions including function calls — abstracting predicates would not cover our shapes.
Constraints
- Idempotent. Re-running migrate against a converged DB MUST be a no-op.
- Drift handling — non-negotiable. If a policy exists in the live DB but not in the migration source, DataProvider MUST drop it. Orphan policies surviving rename refactors are a security regression: a renamed policy without the old one being dropped means two predicates evaluated, one stale.
- Disable when removed. If
enableRowLevelSecurity flips back to false (or a table loses its RLS declaration), DataProvider MUST DISABLE ROW LEVEL SECURITY.
SQL surface NAP currently emits
ALTER TABLE public.agent_configs ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.agent_configs FORCE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS agent_configs_member ON public.agent_configs;
CREATE POLICY agent_configs_member ON public.agent_configs
FOR ALL TO app_user
USING (tenant_id = app_tenant_id() AND is_member(app_user_id(), app_tenant_id()))
WITH CHECK (tenant_id = app_tenant_id() AND is_member(app_user_id(), app_tenant_id()));
DROP POLICY IF EXISTS agent_configs_admin_all ON public.agent_configs;
CREATE POLICY agent_configs_admin_all ON public.agent_configs
FOR ALL TO app_admin
USING (true)
WITH CHECK (true);
Real-world scale
NAP has 13 tenant-scoped tables, each with two policies (*_member + *_admin_all), plus two non-trivial bespoke policies (tenant_members_self_or_owner SELECT-only, api_keys_self_or_writer with different USING vs WITH CHECK predicates). Full pattern in src/nap/db/bootstrap.py _policy_statements.
Priority
NAP Tier 1. Single biggest blocker to retiring the RLS overlay.
If you already have a YAML design in flight, paste 5 lines back and I'll tell you whether it covers the predicate shapes above.
Filed by NimblesiteAgenticPlatform (NAP, agent NapSupport2). NAP is converting from alembic to DataProvider for structural schema NOW; RLS lives in a Python overlay until DataProvider supports it. This issue is NAP's requirements, not a prescription — the YAML schema design is yours.
Problem NAP needs to express
A tenant-scoped table needs to declare:
USING).WITH CHECK) — often the same as USING, sometimes stricter.FOR ALLfor full access, orFOR SELECTonly for read-restricted policies liketenant_members_self_or_owner).TOclause.The predicates contain non-trivial SQL: equality checks against GUC-reader functions (
app_tenant_id()), boolean ANDs with SECURITY DEFINER membership helpers (is_member(app_user_id(), app_tenant_id())),EXISTSsubqueries against parent tables (for indirect tenant scoping viaconversation_id), and OR-combinations (tenant_members_self_or_ownerisuser_id = app_user_id() OR (tenant_id = app_tenant_id() AND is_tenant_owner(...))).NAP needs the predicate model to allow arbitrary SQL expressions including function calls — abstracting predicates would not cover our shapes.
Constraints
enableRowLevelSecurityflips back to false (or a table loses its RLS declaration), DataProvider MUSTDISABLE ROW LEVEL SECURITY.SQL surface NAP currently emits
Real-world scale
NAP has 13 tenant-scoped tables, each with two policies (
*_member+*_admin_all), plus two non-trivial bespoke policies (tenant_members_self_or_ownerSELECT-only,api_keys_self_or_writerwith different USING vs WITH CHECK predicates). Full pattern in src/nap/db/bootstrap.py_policy_statements.Priority
NAP Tier 1. Single biggest blocker to retiring the RLS overlay.
If you already have a YAML design in flight, paste 5 lines back and I'll tell you whether it covers the predicate shapes above.