Row-level security
Row-level security (RLS) filters rows, not tables: two users query the same table and see different data. nexp uses PostgreSQL RLS natively — every request runs under the caller's database role with the caller's identity available to policies, so the same rules hold in the UI, the data API, and server functions.
Enabling RLS on a table
In the data designer, open the table's RLS settings and enable it. The platform enables RLS together with a permissive default policy — never bare RLS, because a table with RLS on and no policy returns nothing.
Writing policies
Policies are plain SQL expressions over the row and the caller. The caller's
identity is available as the app.user_id setting:
-- Owners see and edit their own rows
CREATE POLICY task_owner ON crm.task
USING (owner_id = current_setting('app.user_id')::uuid);
-- Everyone with the app role can read; only managers write
CREATE POLICY task_read ON crm.task FOR SELECT
USING (true);
CREATE POLICY task_write ON crm.task FOR UPDATE
USING (pg_has_role(current_user, 'crm_manager', 'member'));
Checking access from the UI
Widgets don't need special handling — the database filters rows before they reach any widget. When you need an explicit answer ("may this user edit row X?"), use the permission-check endpoint:
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"app": "crm", "table": "task", "ids": ["9f8e…"], "action": "write"}' \
"$DATA_API/v1/acme/permissions/rows"
Notes
- Platform meta tables ship with their own RLS (app-scoped: users see only the apps they can access).
- RLS composes with app roles: schema
USAGEgates the app, grants gate the table, policies gate the rows.