Field restrictions
On this page
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.
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 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)
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. Otherwisepolicy.output_field_unprovednames the condition or order. A transition'swhen:is a write's condition: refusing a record shows what it tests. A lifecycle'svisible:is a condition on every read of its entity, so it tests only fields everyone the entity'sread: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 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, amatchor 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 areturnorrejectit 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 itsset: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_unprovednames 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 arejectin 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). What a log holds is
covered by log redaction in Personal data.
Markdown for agents: /docs/field-restrictions.md.Built from Jadpo main at a707efb.