Owner and shared entities
An entity is declared by its file db/schema/<entity>.toml. What an end user's session may read and write of it
follows from where the user sits in the organization tree, and from two declarations of the entity: owner and
shared. They exclude each other.
The default: at or below the user's node
With neither key, every request of an end user reaches the records of their node and of every node below it. A record is stored at the node the request that created it acts at.
Owned entities
display_name = "Order"
owner = "customer_sub"
owner_bypass_roles = ["admin", "staff"]
[fields.customer_sub]
type = "string"
[fields.total]
type = "decimal"
owner names a string field of the entity that holds the owning end user's identity, one line. An end user then reaches only
the records they own, and a record they create is owned by them whatever the request says. owner_bypass_roles
lists roles of [authenticate] roles whose holders are not owner-scoped on this entity: that is how staff read
every order. aircloud check refuses an owner field that does not exist or is not string, and a bypass role the
vocabulary lacks.
aircloud generate scaffold declares an owned entity with --owner; its bypass roles default to the vocabulary's
roles other than customer:
aircloud generate scaffold order total:decimal --owner customer_sub
Shared entities
display_name = "Product"
shared = true
Records of a shared entity stored at a node above the user's are readable by them — a catalogue kept at the
top of the tree is read by every customer below it. Writes stay at or below the user's node. An owned entity can
never be shared.
Roles each write requires
display_name = "Invoice"
write_roles = { create = ["staff"], update = ["staff", "clerk"], delete = ["admin"] }
write_roles names, per operation, the roles an end user must hold to write a record: each list is any-of, each
role a key of [authenticate] roles (aircloud check refuses another, schema.write_role_undeclared), and an
operation it does not list needs no role. Air-Flex enforces it on every path a write takes: an end user holding none
of the operation's roles at the record's node is refused flex.role_required (403, naming the entity, the
operation and the roles that would admit it). It binds acting end users only, and never restricts reads — owner
and shared decide those.
Labels
An entity's and a field's names are ASCII identifiers. Their label in the product's default locale is written
verbatim with --display-name (the entity) and a field's :display_name=<label> segment:
aircloud generate model product price:decimal:display_name=Preço --display-name Produto
Without one, the label is the name humanized; under a default locale other than English, aircloud i18n missing
lists every such label, since nobody wrote it.
Requests with no end user
A request carrying no end user's session — a public route, a cron or event trigger, aircloud functions invoke,
seeds — has no end user to scope it. A function with no end user — on a public route, an event or a cron — acts at
the node its org_path declares: org_path on the route in config/routes.toml, or on the trigger in its
function.toml. aircloud check refuses an entry without one (route.org_path_required, trigger.org_path_required),
and org_path = "/" acts at the root only because it is written; aircloud functions invoke acts at your own node or at the node below it that --at names
(The acting node and elevation). Such a function is not owner-scoped: it reads every customer's
owned records at or below that node. Protect an owned entity's routes by placing them in [authenticate] (see
Routes).
The records such a function writes are stored at that node. Sessions at a node below read them only when the entity
is shared, and never write them. To let staff change such records, write them from a function that runs in the
staff member's session (a route inside [authenticate]).
Fields a member may see and change
Which fields of a record a member sees, may change and must fill in is declared per field and per record: see Field policy.