> ## Documentation Index
> Fetch the complete documentation index at: https://developers.zite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Permissions

> The complete zite.permissions.json schema — roles, rules, effects, and row filters.

Roles and data-access rules for a workspace live in a `zite.permissions.json` file at the workspace root.
This is the schema reference; for the concepts and evaluation model, see [Roles & permissions](/concepts/permissions).

## Structure

```typescript theme={null}
type ZitePermissionsJson = {
  version: '1.0';
  defaultPolicy?: 'allow' | 'deny';        // for tables with no rules; default 'allow'
  roles: { id: string; name: string; description?: string }[];
  tables: Record<string, { rules: Rule[] }>; // keyed by central-DB table SDK name
};

type Rule = {
  roles: string[];                                     // role names this rule applies to
  operations: ('read' | 'create' | 'update' | 'delete')[];
  effect?: 'allow' | 'deny';                           // default 'allow'
  rowFilter?: RowFilter;                               // limits which rows the rule covers
};

type RowFilter =
  | {
      type: 'comparison';
      field: string;                                   // a field on the table
      operator: 'eq' | 'neq' | 'contains' | 'gt' | 'gte' | 'lt' | 'lte';
      userField?: string;                              // dynamic value from context.user
      staticValue?: string | number | boolean;         // OR a fixed value (exactly one of the two)
    }
  | { type: 'and'; filters: RowFilter[] }
  | { type: 'or'; filters: RowFilter[] }
  | { type: 'not'; filter: RowFilter };
```

## Fields

* **`version`** — always `"1.0"`.
* **`defaultPolicy`** — what happens on a table with no rules: `allow` (open, the default) or `deny` (locked
  unless a rule grants access).
* **`roles`** — each role has a stable `id` and a display `name`. Rules reference roles by **name**; the
  `id` survives renames so member assignments never break. The built-in role
  `{ id: "builtin:all-team-members", name: "All team members" }` applies to every internal member.
* **`tables`** — keyed by the table's SDK name (PascalCase). Each entry has a `rules` array.

## Rules

* **`roles`** — the role names this rule applies to.
* **`operations`** — any of `read`, `create`, `update`, `delete`. Listing several is shorthand for one rule
  per operation.
* **`effect`** — `allow` (default) or `deny`.
* **`rowFilter`** — restricts which rows the rule covers (like a Postgres `USING` clause).

## Row filters

A comparison filter compares a record `field` to either a value from the signed-in user (`userField`) or a
fixed `staticValue` — **exactly one** of the two — using `eq`, `neq`, `contains`, `gt`, `gte`, `lt`, or
`lte`. Compose comparisons with `and`, `or`, and `not`.

`userField` can reference the base user fields (`id`, `email`, `firstName`, `lastName`) plus any fields
synced from your [users table](/concepts/authentication#user-sync).

## Example

```json theme={null}
{
  "version": "1.0",
  "defaultPolicy": "deny",
  "roles": [
    { "id": "builtin:all-team-members", "name": "All team members" },
    { "id": "role_manager", "name": "Manager" }
  ],
  "tables": {
    "Orders": {
      "rules": [
        {
          "roles": ["All team members"],
          "operations": ["read"],
          "rowFilter": { "type": "comparison", "field": "ownerEmail", "operator": "eq", "userField": "email" }
        },
        {
          "roles": ["Manager"],
          "operations": ["read", "update", "delete"]
        }
      ]
    }
  }
}
```

Here every team member can read only the orders they own, while Managers can read, update, and delete all
orders. Because `defaultPolicy` is `deny`, any table without rules is fully locked down.

<Note>
  Permissions are **table- and row-level** and govern the workspace database. There are no field-level
  permissions, and external app users bypass roles entirely — see [Roles & permissions](/concepts/permissions).
</Note>
