Attribute access
Verdict provides dot syntax (user.age > 25) as a concise shorthand for reading entity attributes stored as has_attribute facts inside policy rules.
Storing properties as has_attribute facts
Section titled “Storing properties as has_attribute facts”The standard convention for storing entity properties is the three-term has_attribute(entity, key, value) predicate:
declare has_attribute(User, "age", int);
has_attribute(User:alice, "age", 30);Using has_attribute lets multiple property keys share one predicate and enables dot-syntax lookups. Dedicated predicates (such as clearance_level(user, level)) are also valid when they read more naturally in your domain; see Attributes.
Dot syntax shorthand
Section titled “Dot syntax shorthand”In a rule body, variable.attribute is shorthand for reading a has_attribute(variable, "attribute", value) fact inside a comparison or predicate argument:
// Dot syntax shorthanduser.age > 25
// Equivalent explicit has_attribute lookuphas_attribute(user, "age", age: int) and age > 25Dot syntax is valid in rule-body comparisons and predicate arguments:
// Valid: single variable before '.', used in a comparison or argumentallow(user: User, "edit", doc: Document) if doc.archived = false and user.clearance >= doc.required_clearance;
// Invalid syntax:// - User:alice.clearance >= 2 (receiver must be a variable, not an identity)// - user.profile.email = "a@b.com" (cannot chain multiple dots)// - if doc.archived; (boolean attributes must be compared explicitly)If no matching has_attribute fact exists for an entity and key, the lookup fails. Missing attributes never default to 0, "", or false.
Multiple attribute values and independent lookups
Section titled “Multiple attribute values and independent lookups”Each variable.attribute occurrence performs an independent has_attribute lookup. If an entity has multiple values stored under the same key, separate occurrences can match different values:
independent(user: User) if user.age > 25 and user.age <= 40;If User:bob has two stored ages (20 and 50), user.age > 25 matches 50 and user.age <= 40 matches 20, so independent(User:bob) succeeds even though neither age falls inside (25, 40].
Binding a single value across comparisons
Section titled “Binding a single value across comparisons”When multiple conditions must check the same attribute value, bind the lookup to a variable once and compare that variable:
within_range(user: User) if age = user.age and age > 25 and age <= 40;
// Equivalent explicit has_attribute bindingexplicit_range(user: User) if has_attribute(user, "age", age: int) and age > 25 and age <= 40;Stored "age" values | independent (user.age > 25 and user.age <= 40) | within_range (age = user.age ...) |
|---|---|---|
30 | True | True |
20, 50 | True | False |
| None | False | False |
When an attribute should only ever hold one current value in your fact store, replace previous values atomically on write using batchFacts.
Next steps
Section titled “Next steps”- Compare missing facts with explicit
falsebooleans in Negation and missing facts. - Combine attribute checks with relationship scope in Attributes.
- Replace single-valued facts atomically from your backend in Managing Facts.