# Field restrictions

A field follows its entity's grants unless it says otherwise. Use a
field's own `policy:` when some of a record's fields are more sensitive
than the rest, such as a project's budget. It has the same shape as the
entity's, and it can only take away: a field can't grant anyone more than
its entity does (`policy.field_widens_entity`). A field no client may
read or write says so by name.

```jadpo
entity Project {
    ...
    budget: ProjectBudget {
        policy: {
            read: [CompanyRole.editor]
            update: [CompanyRole.editor]
        }
    }
    import_reference: Text {
        policy: FieldAccess.server_only
    }
}
```

Here viewers may read a project, but only editors may read or change its
`budget`. A field's `policy:` narrows `create`, `read` and `update`. It
has no `delete`, since a field is deleted with its record.

## A field no request may touch: `FieldAccess.server_only`

`FieldAccess.server_only`, as on `import_reference` above, lets no request
read or write the field. A test's or a seed's call without a caller runs
without policy, so it may. [Jobs and events](jobs-and-events.md) says
how schedules and subscribers write one.

## Writes

If a request changes a field the caller may not change, the whole write
is refused; nothing is dropped silently. A caller who may change the
record but not the field gets 403 `not_permitted`. A create that leaves an
optional field `none` sets nothing, so the field's policy never refuses
it.

## Outputs

An output type has one shape for everyone. If anyone allowed to call a
query can't read one of its output fields, the query doesn't compile
(`policy.output_field_unproved`). The fix is a second query with a
narrower output. The same holds for a record a query includes through a
reference: everyone who may read the record must be able to read what it
refers to.

Who may read a value is proved from the grants alone, not from the data.
Two records that each refer to a company may refer to different
companies, so a role held in one says nothing about the other.

## Webhook replies

A webhook's reply
([Webhooks that need a reply](receiving-from-outside.md#webhooks-that-need-a-reply))
goes back to whoever sent the webhook, so it is an output to
`Access.public`. Every field it carries, or that decided it, must be one
anyone at all may read, though its action runs as the system.

The system finds every record its grants name. So a reply that counts or
tests for records shows whether they are there, and they must be records
anyone at all may read. A failure is answered to the sender too. So what
decides whether the action fails (a condition, a `get` or a change by
`id` finding nothing, a `conflict:`) and a failure's client fields are
held to the same proof.

## What else shows a field

A field's value can reach a caller in other ways than an output, and the
compiler checks each like an output:

- **Conditions and orders.** Which records pass a condition, and the order
  they come in, show the fields they test. So a query, `get`, include or
  write may filter or order on a field only when everyone who may run it
  may read that field. Otherwise `policy.output_field_unproved` names the
  condition or order. A transition's `when:` is a write's condition:
  refusing a record shows what it tests. A lifecycle's `visible:` is a
  condition on every read of its entity, so it tests only fields everyone
  the entity's `read:` lets in may read.
- **Copies.** Passing a value through a variable, another type, a
  function or the record a write stores it in doesn't escape the field's
  rules, because the compiler tracks where it came from. A migration's
  [computed backfill](computed-backfills.md) is a copy too: each
  field it reads must be one everyone who may read the field it fills may
  read.
- **What decides a value.** A value made under an `if`, a `match` or a
  loop, or kept by a filter, shows what they test. So it carries their
  fields' rules too. So do a count of what a filter kept, a failure raised
  on such a condition, and what comes after a `return` or `reject` it may
  have taken.
- **Finding records.** Which record a `get`, a query or a write finds
  shows the value it found it by, and an include shows the reference it
  follows. So each is proved like an output.
- **What a write gives back.** The record an update returns is proved
  against its entity's `read:` as well as its fields', since a caller may
  change a record they may not read. What its `set:` gave a field, and a
  created record's values, are the caller's own.
- **What a write stores.** A value stored in a field is read later, from
  any request, by everyone who may read that field. So a create or update
  stores a value in a field only when every one of them may read it,
  proved as an output is. Otherwise `policy.output_field_unproved` names
  the value in the write. A copy between fields the same callers read
  stays allowed: within a record, into a record that refers to the one it
  came from, or into one of the same tenant. Who reads a field is known
  only of the record it is stored in, so a copy into any other record
  proves only what holds of anyone signed in. A patch, `when_supplied:`, a
  counter and a transition store values too, and so does an action a
  value is passed to. What decided a value is stored with it. Since an
  action is all-or-nothing, so is whatever a `reject` in it tests.

## Events

A restricted field read only in an event isn't an output. An event's
subscribers are server work, so `emit` stores nothing a caller reads.
What a subscriber stores from an event is checked where it stores it
([Subscribers](subscribers.md)). What a log holds is
covered by log redaction in [Personal data](personal-data.md).
