# Permissions & row-level security

How SurrealDB's PERMISSIONS clause on tables and fields controls create, select, update, and delete, including per-record and field-level rules using $auth.

SurrealDB lets you declare permissions alongside your schema so access is enforced in the database, not only in application code. You attach a `PERMISSIONS` clause to [`DEFINE TABLE`](/docs/reference/query-language/statements/define/table.md) and [`DEFINE FIELD`](/docs/reference/query-language/statements/define/field.md) to describe which operations are allowed under which conditions.

These clauses apply to [record users](/docs/learn/security/authentication/authentication.md#record-users) and to [guests](/docs/learn/security/authorization/capabilities.md#guest-access) when guest access is enabled. [System users](/docs/learn/security/authentication/authentication.md#system-users) are not restricted by table or field `PERMISSIONS`, using roles instead.

## Defaults: `NONE` and `FULL`

On a table, omitting `PERMISSIONS` results in a `PERMISSIONS NONE` in the actual statement passed to the database: record users cannot `SELECT`, `CREATE`, `UPDATE`, or `DELETE` records in that table until you grant access. `PERMISSIONS FULL` allows all four operations.

Field permissions default the other way. A field without an explicit clause is stored as `PERMISSIONS FULL`. The table is the main access gate, while field permissions only narrow further when you need to (for example hiding a password). With `FULL`, a field follows the table's rules without adding its own.

## Record and field rules

For each table, you set independent rules for **create**, **select**, **update**, and **delete**. Each clause is a SurrealQL expression that must succeed for that operation to proceed; if it fails, the operation is rejected for the affected records. **Field-level permissions** refine this: you can constrain **select**, **create**, and **update** on individual fields - useful for sensitive data that should not be readable or writable under the same rules as the rest of the record.

What the wider industry calls **row-level security** is implemented here by writing expressions that depend on the authenticated context. The **`$auth`** variable holds the current identity and claims after sign-in, so you can compare it to fields on the record (for example `owner = $auth.id`) and ensure each user only sees or changes their own data.

### Example: users read only their own records

```surql
DEFINE TABLE order SCHEMALESS
	PERMISSIONS
		FOR select
			WHERE customer = $auth.id
		FOR create
			WHERE customer = $auth.id
		FOR update, delete
			WHERE customer = $auth.id
;
```

### Example: a field only administrators may update

```surql
DEFINE FIELD internal_note ON order TYPE string
	PERMISSIONS
		FOR select FULL
		FOR update WHERE $auth.role = 'admin'
;
```

Together, table- and field-level `PERMISSIONS` give you flexible authorisation without duplicating policy in every client. For full syntax and options, see [`DEFINE TABLE`](/docs/reference/query-language/statements/define/table.md#defining-permissions) and [`DEFINE FIELD`](/docs/reference/query-language/statements/define/field.md#setting-permissions-on-fields).
