In relationship-based access control, what does one relation tuple `object#relation@subject` record, and what replaces a role column?
answer
- one fact per row, three fields
- object, relation, subject
- the subject may be a set
- the schema says which relations imply which
- no role column anywhere
basics
~20 sA relation tuple records one fact — this subject holds this relation on this object, written object#relation@subject. Permission becomes the set of stored tuples plus the schema's rules about which relations imply which, instead of a role value stored on the user row.
solid answer
~50 sA relation tuple is the whole unit of storage: `object#relation@subject` reads as *this subject holds this relation on this object*. In an aviation maintenance record system, `airframe:G-ABCD#inspector@user:15` says engineer 15 is an inspector on that airframe. The object is a typed identifier, the relation is an edge name declared in the schema for that type, and the subject is either another typed identifier or a **userset** — everyone holding some relation on some other object, written `org:northfield#member`. There is no role column and no per-resource access list with its own shape; every fact looks the same, so one traversal engine serves all of them. A tuple is not itself a decision: the schema says which relations imply which, and an authorization question is answered by asking whether a path exists from the subject to the object.
code
pseudocode · 8 lines// the whole authorization state, one fact per line
airframe:G-ABCD#holder@org:northfield
org:northfield#member@user:15
airframe:G-ABCD#inspector@user:15
component:hyd-pump-77#airframe@airframe:G-ABCD
// a userset subject: everyone holding `member` on the organisation
airframe:G-EFGH#inspector@org:northfield#membergo deeper
Be able to read airframe:G-ABCD#inspector@user:15 aloud as a sentence and name the three fields. Knowing that the subject can be a whole set, not just one person, is the part that separates a real answer from a guess.
Explain why every fact having the same shape matters: one engine indexes and traverses all of them, and a new resource type costs a schema entry rather than a new table and a new query.
Show that you know a tuple is a fact and not a decision, and that revocation means deleting a fact rather than setting a flag. Say where the object and subject identifiers come from in a real request.
The question worth owning is what the uniform shape buys over years: a vocabulary the store can enforce, one audit surface, and resource types that arrive without a new access-control schema each time.
## The unit of storage In relationship-based access control the authorization state of a whole system is one set of small, uniform facts called **relation tuples**. A tuple is conventionally written `object#relation@subject` and read left to right as *this subject holds this relation on this object*. Nothing else is stored for authorization purposes: no `is_admin` flag on the user row, no `role` column, no bespoke access-list table per resource type. Take an aviation maintenance record system, where authority to sign off work follows the airframe a component is fitted to, and the airframe follows the maintenance organisation that holds it. The fact that engineer 15 is an inspector on airframe `G-ABCD` is the tuple `airframe:G-ABCD#inspector@user:15`. The fact that a hydraulic pump is fitted to that airframe is `component:hyd-pump-77#airframe@airframe:G-ABCD`. Both are the same shape, and so is every other fact in the store. That uniformity is the point. Because every fact has three fields and no others, one indexing and traversal engine serves every resource type in the product, and a new resource type is a schema addition rather than a new table with its own query. ## The three fields | field | what it holds | example | |---|---|---| | object | a typed identifier for the thing being protected | `airframe:G-ABCD` | | relation | an edge name declared in the schema for that object type | `inspector` | | subject | a typed identifier, or a userset | `user:15` or `org:northfield#member` | - The **object** is typed, so `component:17` and `airframe:17` are different things and can never be confused by a check. - The **relation** is not free text. Each object type declares the relations it supports, and writing a tuple with an undeclared relation is rejected — which is how the store stops the vocabulary drifting the way a string permission column does. - The **subject** is the field that carries the model's real power, because it does not have to name one principal. ## A subject can be a set, not just a user A subject written with a `#` — `org:northfield#member` — is a **userset**: *everyone who holds `member` on `org:northfield`*, whoever that turns out to be at the moment of the question. So the grant `airframe:G-EFGH#inspector@org:northfield#member` is a single stored tuple that covers a four-hundred-person organisation, and adding or removing a member changes who is covered without touching the grant at all. This is why group membership is not a special case in the model. A group is just an object with a `member` relation, and membership is an ordinary tuple. ## What the schema contributes Stored tuples are the **direct** facts. The schema adds the rules that produce **derived** facts at read time: - a relation can be satisfied by another relation on the same object (anyone holding `inspector` also satisfies `signoff`); - a relation can be satisfied by following a tuple to a different object and taking a relation there (a component's `signoff` is the `inspector` of the airframe it is fitted to). So the answer to an authorization question is not a lookup of a stored row. It is: *does a path exist, under the schema's rules, from this subject to this object through the tuples that are actually stored?* ## What one tuple does not carry 1. **No expiry and no condition.** The basic tuple is a bare fact. Time windows, attribute tests and environmental conditions are a layer above the relationship answer, not a field on the tuple. 2. **No priority or ordering.** Tuples are a set, not a rule list evaluated top to bottom, so there is no first-match ordering to get wrong. 3. **No statement that the subject exists.** The store holds identifiers; it does not know your user table and does not authenticate anyone. Your service still establishes who the caller is before it asks a question about them. 4. **No verdict.** `airframe:G-ABCD#inspector@user:15` does not say *allowed*. It says a relationship holds. Whether that relationship grants sign-off is the schema's business. ## What this means for the server author Granting becomes writing a tuple at the moment the relationship is created in your domain — an engineer is assigned to an airframe, a component is fitted, an organisation takes custody. Revoking becomes deleting that tuple rather than clearing a flag. And the request-time question becomes a call to the store with the object taken from the route you are serving and the subject taken from the authenticated session, asking for one boolean.
- If a tuple carries no verdict, what actually decides that engineer 15 may sign off the hydraulic pump?The schema plus a traversal. The schema declares that a component's `signoff` relation is satisfied by the `inspector` relation on the airframe reached through the component's `airframe` tuple. The store follows that path over the stored tuples and returns a boolean. Your endpoint enforces it; the store only answers.
- Why does the notation put a `#` on both sides in `airframe:G-EFGH#inspector@org:northfield#member`?Both sides are the same construction. `airframe:G-EFGH#inspector` names a relation on an object; `org:northfield#member` names a relation on an object too, and using it as the subject means *every subject holding that relation*. The `@` separates what is being granted from who receives it.
- How is a grant revoked in this model?By deleting the tuple that expressed it. There is no negative tuple and no deny flag in the basic model, so removing authority means removing the fact, or removing a tuple further up the path — taking custody of the airframe away from the organisation revokes everyone who held authority only through it.
saying these in an interview costs you the question
- Says the subject field can only ever name one user
- Thinks every derived grant must exist as its own stored tuple
- Calls the middle field a permission string rather than a declared relation
- Assumes the tuple itself carries an expiry or a condition
- Believes the tuple store also authenticates the caller