Query-Scoped Permissions
Any grant in a role’s permission matrix can be narrowed with a query predicate — a condition the resource must satisfy for the grant to apply. This turns coarse permissions like Items: Edit into precise rules like “may edit items, but only safety-critical ones” or “may edit items, but only their own drafts”.
Query-scoped grants show as 🔍 in the permission matrix. Click a ✓ cell to convert it, or right-click a cell to open the predicate editor directly.
The syntax is the item search syntax
Predicates use the same query language as the item search input — if an expression works as a search filter, it works as a permission predicate. For example:
custom.safety_critical = true
status = "draft" AND created_by = $user.id
custom.asil IN ("C", "D")
Referring to the current user
Predicates can reference the acting user with $user variables, which makes one rule serve every user:
| Variable | Resolves to |
|---|---|
$user.id | The user’s ID, e.g. alice |
$user.email | The user’s email address |
$user.display_name | The user’s display name |
created_by = $user.id is the classic example: every holder of the role may edit exactly the items they created themselves.
Referring to custom fields
Prefix custom fields with custom. to distinguish them from built-in fields: custom.safety_critical = true, custom.asil IN ("D", "C").
Worked examples
| Goal | Grant |
|---|---|
| Authors may edit only their own drafts | Items: Edit where created_by = $user.id AND status = "draft" |
| Safety engineers may edit and approve only safety-critical items | Items: Edit and Items: Approve where custom.safety_critical = true |
| A supplier sees only items assigned to them | Items: View where assignee = $user.id |
How predicates are evaluated
Three rules cover almost everything you will observe:
- An unconditional grant wins. If the user holds an unconditional ✓ for the same permission from any role, query-scoped grants for that permission are irrelevant — the action is allowed everywhere. You cannot use a predicate to restrict a permission another role already grants unconditionally; permissions are additive only.
- Any matching predicate allows. If all of a user’s grants for a permission are query-scoped, the action is allowed when at least one predicate matches the resource.
- Lists are filtered. Predicates don’t just gate single actions — a query-scoped Items: View filters item lists and search results, so the user only ever sees the items their predicate allows.
Note: A predicate that references an unset attribute matches nothing. For instance,
assignee = $user.emailfor a user with no email simply never matches — it does not error, and it does not fall back to allowing everything.
Authoring tips
- Prefer exact comparisons (
custom.team = "safety") over contains-matches (custom.tag ~ "safety") — they are faster and easier to reason about. - Validate before rollout: use the Effective Permissions inspector with a concrete resource ID to confirm a predicate matches (or doesn’t) exactly as intended.
- Reuse via inheritance: if many grants need the same predicate, put them on one parent role and let other roles inherit it.