# Background work

Schedules and event subscribers are the work your app does without a
request ([Jobs and events](jobs-and-events.md)). They run as the system
caller, which gets exactly what `Access.system` grants. To let them read or
change an entity's records, name `Access.system` in its lists, such as
`update: [owner_id, Access.system]`.

The system is nobody signed in, and it holds no membership. So:

- a user field, a role and `Access.authenticated` never let it in;
- a grant list, a field's own list or an `actions:` line lets it in only
  by naming `Access.system`.

## `Access.public` doesn't let background work in

An entity grants background work explicitly. A schedule, a subscriber or
a batch that reaches a statement only `Access.public` grants is a compile
error, `policy.system_grant_missing`, in the same way that
`policy.effect_ungranted` refuses such a route.

## Which tenant background work reaches

On a tenant's records ([Tenancy](tenancy.md)), a grant naming `Access.system`
lets in:

- a schedule's run in every tenant, since no tenant's event starts it;
- a subscriber within the tenant its event names, in the event's field
  of the tenant's `id` type;
- a batch's run within the tenant its saved values of that type name.

A subscriber or a batch whose value names no tenant reaches no tenant's
records, only those every tenant shares. A value names no tenant when
there is none, when it is `none`, or when its values name different
tenants. So "no tenant" means every tenant for a schedule, which names
none by design, and none for work that names its tenant by a value. [Jobs
and events](12-jobs-and-events.md) covers this under "Background work and
tenants".

The system never moves a record to another tenant, which needs a
membership there, and never founds a tenant's first membership.

## The system and `actions:` lines

A line under `actions:` that names `Access.system` lets the system in
beside whoever else the line names. A line naming only `Access.system`
still lets in no request.

No request is ever the system. Only the runtime's runner makes the system
caller. Every request, and every call a test makes, with a caller or
without one, runs as someone else.

## Actions the system runs inside a request

The runtime runs three of your app's actions as the system, even inside a
request:

- the magic-link `send:` action ([Magic links](magic-links.md)),
  delivered as a subscriber of `jadpo.MagicLinkEmail` with its delivery's
  tenant, which is none;
- a webhook's reply action
  ([Webhooks that need a reply](receiving-from-outside.md#webhooks-that-need-a-reply));
- the `on_first_sign_in:` action, which [Identity](identity.md) runs
  for a person the app hasn't seen before.

None of them reaches a tenant's records, only those every tenant shares,
since no tenant's event starts them and "no tenant" means every tenant
only for a schedule. Each is let in only by `Access.system`. So when one
of them reaches a grant, or a line under `actions:`, that names
`Access.public` and not `Access.system`, that is
`policy.system_grant_missing`, reported where the action is named.

## Proving which tenant work is for

A subscriber or a batch's run works in the tenant a value names: the
event's field of the tenant's `id` type, or what the run saves of it. A
caller could pass the id of a tenant they don't belong to. So that value
must come from a record of that tenant the action read or wrote under its
caller's policy. Otherwise it is `policy.event_tenant_unproved`, reported
at the value.

The usual fix is to read the tenant first,
`attempt Household.get(household_id)`, or to name the tenant of a record
the action changes. In full, a value proves its tenant when it is one of
these:

- the tenant's own `id`, or the tenancy reference of one of the tenant's
  records, that the action, or an action or query it calls, read, created
  or changed. The tenancy reference is the field that ties a record to
  its tenant, such as `company_id`.
- the same, of a record the action is handed whole, which counts as read.
  A route passes path placeholders, `input` and the caller, and a
  subscriber passes its event's fields. So only a body that read a record
  with the same caller, or a run limited to its tenant, can hand one to an
  action. For example, a batch's action
  `invoice_round(rent_round: RentRound)` names its run's society from
  `rent_round.society_id`.
- the value a create sets a record's tenancy reference from, when the
  create runs whenever the action does. The create needs the caller's
  membership in that tenant, and the action fails with it. A create
  inside an `if`, a `match` or a loop, or after a `return` that may leave
  first, proves nothing.

Anything else is refused, including a parameter that holds an id. Only
where the value came from counts, not what decided it.

The check runs in the action whose `emit` or `batches.each` uses the
value, never at its callers. An action that emits from a parameter is
refused even when every action calling it passes a proved id, since a
route may call it too. Pass it the record instead. The same rule holds
for every action, so a subscriber's action that passes its event's tenant
on, to another `emit` or a batch, reads a record of that tenant first, as
[Declaring an event](declaring-an-event.md) advises
for anything more than ids.

What a batch's run stores, from its items, what it saved and its
summary, is proved as a call's arguments are, by the tenant the run is
limited to. What `batches.status` gives is held to what decides every
batch's failures. [Jobs and events](jobs-and-events.md) covers this
under "What a run reaches under policy".
