# Permission rules for an internal tool - scava.json

To control who may see and change what in an internal tool, put a file named `scava.json` next to `index.html`. It says, per collection of entries, who reads and who writes. Scava enforces these rules on its own server for every request, so they hold even if someone edits the page in the browser or sends requests by hand. The app only shows or hides buttons; it is never the place where permissions are decided.

## The file

```json
{
  "name": "Team vacation",
  "data": {
    "vacation": { "read": "all", "write": "own" }
  },
  "files": {
    "receipts": { "read": "own", "write": "own" }
  }
}
```

- `name` is the display name of the app.
- `data` holds the rules for [database](/docs/database) collections, `files` the rules for [file](/docs/files) collections. Use the collection name as the key. A collection name of `"*"` is the rule for every collection that has none of its own.
- A collection without a rule, and no `"*"`, gets the default: **read: all, write: own**.
- Server code has its own settings in the same file: `server` with `schedules` and `webhooks`. See [Schedules and webhooks](/docs/schedules-webhooks).

## The four values

| Value | For `read` | For `write` |
|---|---|---|
| `"all"` | Everyone who can open the app reads all entries. | Everyone changes and deletes all entries and adds new ones. |
| `"own"` | Each person reads only the entries they created. | Each person adds entries and changes or deletes only the entries they created. |
| `"owner"` | Only the creator of the app reads. | Only the creator adds, changes and deletes. |
| `"group:<name>"` | Only members of that group read. | Members of that group add, change and delete every entry. |

"Own" means the entry's `created_by` is the person's email address.

The **creator of the app** can always read and change everything. A **co-editor** gets no more than the rules give any other person; they can, however, publish new rules.

### Lists: any one is enough

A list means a person needs only one of the values.

```json
"read": ["own", "group:hr"]
```

Each person sees their own entries, and members of `hr` see all of them. Keep lists short; a list with too many values is refused.

### Groups

`group:<name>` refers to a group in the account of the app's owner, for example `group:leads`. The owner creates groups on the Overview page at scava.ai, in the browser. Agents cannot create groups. Group membership is read fresh on every request, so removing someone from a group takes effect at once.

If a version names a group that does not exist yet, publishing works and tells the agent which group is missing. Until the owner creates it, only the owner can do what the rule allows the group.

## Rules for single fields

Inside a collection, `fields` sets rules for single fields of an entry. They use the same values.

```json
"vacation": {
  "read": "all",
  "write": ["own", "group:leads"],
  "fields": {
    "status": { "write": "group:leads" }
  }
}
```

Field rules can only take away. They never give more than the rule of the collection.

- A field someone may not **read** is left out of every entry they receive: in lists, single entries and the answers to add and update.
- A field someone may not **write** cannot be set, changed or removed by them. Sending the field back unchanged is fine, so people can keep editing the rest of their entry.
- `own` for a field means "only on entries the person created".
- Field names use letters, digits, `-` and `_`. Names like `__proto__` are not allowed.
- Files have no field rules.

### Example 1: a vacation planner where team leads approve

```json
{
  "name": "Team vacation",
  "data": {
    "vacation": {
      "read": "all",
      "write": ["own", "group:leads"],
      "fields": { "status": { "write": "group:leads" } }
    }
  }
}
```

What this does:

- Everyone sees all vacation entries, including the `status`.
- Everyone adds an entry for themselves and edits or deletes their own. They can write `from`, `to` and a note, but they cannot set `status`.
- Members of `leads` can change any entry, and only they can set `status`. In the app they click "Approve", which updates `{ status: "approved" }`.
- Everyone else who tries to set `status`, by hand or with a changed page, gets "Only members of leads and the owner of this app can set "status"."

The app can hide the "Approve" button for everyone who is not a lead, with `scava.me()` returning `groups`. The button is only a convenience. The rule is the lock.

### Example 2: salaries that only the person and HR see

```json
{
  "name": "Staff directory",
  "data": {
    "staff": {
      "read": "all",
      "write": ["own", "group:hr"],
      "fields": {
        "salary": { "read": ["own", "group:hr"], "write": "group:hr" }
      }
    }
  }
}
```

Each employee creates one entry about themselves, with name, team and phone. HR adds the `salary` to any entry.

- Everyone reads the directory. The `salary` field is missing from every entry except the person's own entry, and for members of `hr`, who see it everywhere.
- Employees edit their own entry (phone, team) but cannot set or change `salary`, even on their own entry.
- HR changes any entry and is the only group that writes `salary`.

Because "own" means "created by this person", the employee must be the one who creates their own entry. If HR created the entries, nobody but HR would see the salaries.

## Rules for files

`files` uses the same values, with these differences: it has no `fields`, and the rule says who sees the list and downloads and who uploads. See [Files](/docs/files).

## Rules apply to server code too

[Server code](/docs/server-code) acts with the rights of the owner by default. Use `env.SCAVA.db('vacation', { as: 'me' })` to act with exactly the rights of the person who asks. Then the rules above apply to that person, groups and fields included. Without a person (a webhook or a schedule), `as: 'me'` is refused.

## Checks at publish time

Scava refuses a `scava.json` that is not valid JSON, has an unknown rule value, a bad collection name or bad field rules, and says what is wrong. The error text of a refused request tells the person who may do it, for example "Only members of hr and the owner of this app can add entries here."
