Row-Level Security (RLS)

RLS policies filter rows transparently on every query. No application code changes needed.

CREATE RLS POLICY user_orders ON orders FOR READ USING (customer_id = $auth.id);
CREATE RLS POLICY user_write ON orders FOR WRITE USING (customer_id = $auth.id);
CREATE RLS POLICY admin_bypass ON orders FOR READ USING ($auth.role = 'admin' OR customer_id = $auth.id);
CREATE RLS POLICY org_access ON projects FOR ALL USING (org_id = $auth.org_id);

Policy Types

TypeApplies to
READSELECT
WRITEINSERT, UPDATE, DELETE
ALLBoth

Permissive vs Restrictive

Multiple policies: permissive (default, OR-combined) or restrictive (AND-combined):

CREATE RLS POLICY org_filter ON docs FOR READ USING (org_id = $auth.org_id) RESTRICTIVE;
CREATE RLS POLICY not_deleted ON docs FOR READ USING (status != 'deleted') RESTRICTIVE;

Session Context Variables

RLS policies can reference the authenticated session context via $auth.* variables:

VariableTypeDescription
$auth.idstringUser ID (from JWT sub or user record)
$auth.usernamestringUsername
$auth.rolestringCurrent role
$auth.tenant_idu64Tenant ID of the authenticated user
$auth.database_idu64Database ID of the session

Example combining tenant and database scoping:

CREATE RLS POLICY multi_scope ON data FOR READ
  USING (tenant_id = $auth.tenant_id AND database_shard = $auth.database_id);

Substitution is fail-closed: if the auth context lacks a required variable (e.g., $auth.database_id is null), the query rejects the row rather than allowing wide-open access.

Cross-Engine Behavior

RLS filters are injected at plan time, before engine dispatch:

  • Vector search — unauthorized vectors excluded from results
  • Graph traversal — edges to invisible nodes are skipped
  • FTS — only matching documents passing RLS are returned
  • KV — blocked rows return empty

Managing

SHOW RLS POLICIES;
DROP RLS POLICY user_orders ON orders;