# Write your first row-level security policies

> A hands-on guide to Postgres row-level security on Scribase: enable RLS, write a per-user policy, add tenant isolation, keep policies fast.

Source: https://scribase.com/guides/write-row-level-security-policies

Reading time: ~6 min.

Row-level security moves access control into Postgres, so a user only ever sees rows they are allowed to read — enforced once, honored by REST, realtime, and SQL alike. This guide enables RLS on a table, writes a per-user policy, adds tenant isolation, and shows how Scribase lints policies for cost.

## Enable RLS on the table

Row-level security is off by default. Once enabled, a table denies all access until a policy grants it, which is the safe starting point.

```sql
alter table todos enable row level security;
```

## Let users read and write their own rows

A policy uses the authenticated user id from the JWT to scope rows. This one lets a signed-in user see and change only the todos they own.

```sql
create policy "own todos"
  on todos
  for all
  using (user_id = auth.uid())
  with check (user_id = auth.uid());
```

## Add tenant isolation for a SaaS schema

For multi-tenant apps, scope by an org claim carried in the JWT so a member of one organization can never read another organization’s rows.

```sql
create policy "tenant isolation"
  on projects
  using (org_id = (auth.jwt() ->> 'org_id'));
```

## Keep the policy fast

The common failure is not a wrong policy but a correct one that scans under load. Scribase lints policies for cost so you catch an expensive predicate before it reaches production; index the columns your policies filter on.

```sql
create index on projects (org_id);
```

## FAQ

**Does a policy apply to the REST API and realtime?**

Yes. Because policies live in Postgres, the /rest data API, realtime subscriptions, and direct SQL all enforce the same rules — there is no second access layer to keep in sync.

**How do I know a policy will stay fast?**

Scribase lints policies for cost so you catch an expensive predicate early, and you index the columns your policies filter on so scoping does not turn into a scan.

## Related

- [Features](https://scribase.com/features)
- [Scribase for SaaS](https://scribase.com/for/saas)
- [SaaS backend use case](https://scribase.com/use-cases/saas-backend)