Row-Level Security
Pulsabase allows you to define Row-Level Security (RLS) policies directly within your TypeScript models. Policies are enforced at the PostgreSQL database level — not in application code — meaning they cannot be bypassed by any SDK call, raw query, or misconfigured backend.
How It Works
Section titled “How It Works”- You define access rules in the
policiesarray of yourstatic schema - The Pulsabase CLI generates the corresponding
CREATE POLICYstatements - On
pulsabase db:sync, policies are applied to your PostgreSQL database - Every SDK query is automatically filtered — no application code required
Defining Policies
Section titled “Defining Policies”A policy defines which action it applies to, the required role or permission, an owner matching column, or a public flag.
import { ModelSchema, primary, text, integer } from 'pulsabase';
export default class Note { @primary id: string;
@text() title: string;
@text() content: string;
@text() user_id: string; // Foreign key to the owner
@boolean() is_public: boolean;
static schema: ModelSchema = { tableName: 'notes', timestamps: true, policies: [ // 1. Any authenticated user can create a note { action: ['INSERT'], authenticated: true },
// 2. Only the owner can read, edit, or delete their private notes { action: ['SELECT', 'UPDATE', 'DELETE'], authenticated: true, owner: 'user_id' },
// 3. Admins can delete any note { action: ['DELETE'], role: 'admin' },
// 4. Public notes can be read by anyone (including unauthenticated users) { action: ['SELECT'], public: true, pivotWhere: { is_public: true } }, ] };}Policy Properties Reference
Section titled “Policy Properties Reference”Logic between fields in a single policy object is AND. A user must satisfy all specified conditions.
Logic within arrays (e.g., role: ['admin', 'editor']) is OR — any matching role grants access.
| Property | Type | Description | Generated SQL Condition |
|---|---|---|---|
action | string[] | SQL commands: SELECT, INSERT, UPDATE, DELETE | FOR SELECT / INSERT / ... |
authenticated | boolean | Applies to any signed-in user | current_setting('request.jwt.sub') IS NOT NULL |
role | string | string[] | Required IDP role | current_setting('request.jwt.role') = 'admin' |
permission | string | string[] | Required IDP permission | current_setting('request.jwt.permissions') @> '"rooms.create"' |
owner | string | Column that must match the JWT sub (user ID) | user_id = current_setting('request.jwt.sub') |
public | boolean | Applies to everyone, including anonymous users | TRUE |
pivotWhere | object | Additional row-level condition on top of the access check | AND is_public = true |
Authentication Modes
Section titled “Authentication Modes”Configure how Pulsabase resolves the current user in Dashboard → Settings → Auth Mode:
| Mode | Description |
|---|---|
IDP | Use Pulsabase’s built-in Identity Provider (recommended). JWTs are signed and verified automatically. |
JWKS | Use an external provider (Auth0, Clerk, Firebase, Supabase, etc.). Pulsabase fetches the JWKS endpoint to validate external JWTs. |
NONE | RLS is disabled entirely for the project. Development only. |
Deploying Policies
Section titled “Deploying Policies”Pulsabase supports two-way binding for RLS policies.
Code → Database (db:sync)
Section titled “Code → Database (db:sync)”If you define your policies in TypeScript models:
# Preview the generated CREATE POLICY statementspulsabase db:sync --dry-run
# Apply policies to the databasepulsabase db:syncDatabase → Code (db:pull)
Section titled “Database → Code (db:pull)”If you create policies visually from the Dashboard, pull them to your local code:
pulsabase db:pullPulsabase introspects your PostgreSQL database and populates the policies array in your local model files.
Common Policy Patterns
Section titled “Common Policy Patterns”Per-User Data (Notes, Profiles)
Section titled “Per-User Data (Notes, Profiles)”policies: [ { action: ['INSERT'], authenticated: true }, { action: ['SELECT', 'UPDATE', 'DELETE'], authenticated: true, owner: 'user_id' },]Public Read, Authenticated Write
Section titled “Public Read, Authenticated Write”policies: [ { action: ['SELECT'], public: true }, { action: ['INSERT', 'UPDATE', 'DELETE'], authenticated: true },]Admin-Only Management
Section titled “Admin-Only Management”policies: [ { action: ['SELECT'], authenticated: true }, { action: ['INSERT', 'UPDATE', 'DELETE'], role: 'admin' },]Permission-Based Access
Section titled “Permission-Based Access”policies: [ { action: ['SELECT'], authenticated: true }, { action: ['INSERT'], permission: 'content.write' }, { action: ['DELETE'], permission: 'content.delete' },]