Skip to content

Attributes

Access often depends on properties of the entities involved in addition to their roles and relationships, such as requiring an organization member to hold a sufficient security clearance to read a sensitive document. In Verdict, you store those properties as facts and compare them inside your rules.

Modeling property facts with has_attribute

Section titled “Modeling property facts with has_attribute”

The has_attribute(entity, key, value) convention records an entity property as a three-term fact. You can specify concrete key strings in declare declarations to document the expected value type for each attribute key:

declare has_attribute(User, "clearance", int);
declare has_attribute(Document, "required_clearance", int);
has_attribute(User:alice, "clearance", 3);
has_attribute(Document:handbook, "required_clearance", 2);

Dedicated predicates such as clearance_level(user, level) are also valid when a specific property is central to your domain vocabulary. Use has_attribute when multiple attributes benefit from a shared predicate and dot-syntax lookups.

To compare an actor’s property against a resource’s threshold, match both facts and compare their values:

has_attribute(user, "clearance", user_level: int) and
has_attribute(document, "required_clearance", required_level: int) and
user_level >= required_level

Both facts must be present for the comparison to succeed; missing facts never default to 0 or false. Because Verdict integers are signed 64-bit whole numbers, represent fractional or time-based domain quantities in a consistent integer base unit (such as cents for currency or epoch seconds for timestamps).

Combining attribute checks with relationships

Section titled “Combining attribute checks with relationships”

Layer attribute comparisons onto relationship checks with and so that neither condition grants access on its own:

allow(user: User, "read", document: Document) if
belongs_to(document, organization: Organization) and
member(user, organization) and
has_attribute(user, "clearance", user_level: int) and
has_attribute(document, "required_clearance", required_level: int) and
user_level >= required_level;
  • An organization member whose clearance is below required_level is denied.
  • An outside user with high clearance is denied because they do not belong to the document’s organization.

Writing a separate allow rule that checked clearance without belongs_to and member would create an independent grant path, allowing cleared users to read documents across every organization. See Combining conditions for how additive rules interact, and Permissions for applying resource-qualified guards across multiple access paths.

Dot syntax (variable.attribute) is a concise shorthand for has_attribute lookups inside rule bodies:

meets_clearance_shorthand(user: User, document: Document) if
user.clearance >= document.required_clearance;
  • Multivalued attributes: Each variable.attribute expression performs an independent lookup. When multiple comparisons must check the same attribute value (such as a numeric range), bind the value to a variable once; see Attribute access.
  • Single-valued updates: To overwrite an existing attribute value in Verdict Server, delete the old value and insert the new one atomically with batchFacts.
  • Boolean flags vs. absence: See Negation and missing facts for when to use not is_archived(document) versus document.archived = false.