
# Table Permissions

Sovrium controls record access with **role-based access control (RBAC)** at the table level, **field-level permissions** for granular column control, and optional **row-level predicates** for per-row scoping. Unauthorized access returns **404** (never 403) to prevent record enumeration.

## Permission Values

Every operation accepts one of three formats:

| Value                 | Meaning                                       |
| --------------------- | --------------------------------------------- |
| `all`                 | Everyone, including unauthenticated visitors. |
| `authenticated`       | Any logged-in user.                           |
| `['admin', 'editor']` | Only the listed role names (an array).        |

The three built-in roles are `admin`, `member`, and `viewer` (highest to lowest). Custom role names are also accepted in the array form.

## Table-Level Permissions

Set `permissions` on a table to gate each operation.

| Operation         | Controls who can…                                  |
| ----------------- | -------------------------------------------------- |
| `read`            | View records.                                      |
| `comment`         | Add comments to records.                           |
| `create`          | Create new records.                                |
| `update`          | Modify existing records.                           |
| `delete`          | Soft-delete records.                               |
| `restore`         | Restore soft-deleted records.                      |
| `permanentDelete` | Permanently remove soft-deleted records.           |
| `fields`          | Per-field read/write permissions (see below).      |
| `inherit`         | Inherit all permissions from a named parent table. |

```yaml
permissions:
  read: all
  comment: authenticated
  create: [admin, editor]
  update: [admin, editor]
  delete: [admin]
  restore: [admin]
  permanentDelete: [admin]
```

## Field-Level Permissions

Restrict read/write access to specific columns under `permissions.fields`. Each entry names a `field` and optionally its `read` and `write` audiences (same three formats). When a field permission is omitted, it inherits from the table-level `read` (for read) or `create`/`update` (for write).

| Property | Description                                                       |
| -------- | ----------------------------------------------------------------- |
| `field`  | Name of the field this permission applies to.                     |
| `read`   | Who can read (SELECT) this field. Inherits table `read` if unset. |
| `write`  | Who can write (INSERT/UPDATE) this field. Inherits if unset.      |

```yaml
permissions:
  read: authenticated
  fields:
    - { field: salary, read: [admin, hr], write: [admin] }
    - { field: department, read: all, write: [admin] }
```

## Row-Level Permissions

Set `rowLevelPermissions` for defense-in-depth scoping. Each CRUD operation can carry a server-side `when` predicate that is appended as a filter to every record-returning request. The role gate runs first; the row-level predicate then filters within the permitted role's scope.

A predicate is `{ field, operator, value }`:

| Field      | Description                                                                                                  |
| ---------- | ------------------------------------------------------------------------------------------------------------ |
| `field`    | The table field, or a relation chain like `project.client_id`, to filter on.                                 |
| `operator` | `eq`, `neq`, or `in` (array membership).                                                                     |
| `value`    | A literal (string/number/boolean/array) **or** a `$currentUser` reference resolved per-request from session. |

```yaml
rowLevelPermissions:
  read:
    when:
      field: client_id
      operator: in
      value: { kind: currentUser, path: { kind: assignment, tableSlug: clients } }
  write:
    when:
      field: client_id
      operator: in
      value: { kind: currentUser, path: { kind: assignment, tableSlug: clients } }
```

:::callout
**404, not 403 — anti-enumeration.** When a request fails authorization (table-level, field-level, or row-level), Sovrium returns **404 Not Found** rather than 403 Forbidden. This prevents attackers from discovering which records or tables exist by probing for status codes.
:::

## Resource:Action Permissions

For broader permission contexts (admin plugin, API keys), Sovrium also supports a `resource: [actions]` map where each resource lists allowed actions and `*` means all actions:

```yaml
users: [read, list]
posts: [create, read, update, delete]
analytics: ['*']
```
