Skip to content
Air-Cloud docs

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.