Documentation

Identity & access

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 USAGE gates the app, grants gate the table, policies gate the rows.