Skip to content

Predicates, rules, and facts

Verdict separates what is currently true in your application (facts) from the access control logic that decides what those relationships permit (rules). Both facts and rules use a shared vocabulary of named statements called predicates.

A predicate is a named statement over 1 to 3 positional terms that evaluates to true or false:

// Unbound pattern matching across any actor, action, and resource
allow(user, action, document)
// Partially bound pattern for a specific action and resource
allow(user, "read", Document:handbook)
// Fully bound statement with concrete values in every term position
is_owner(User:alice, Document:handbook)

Each expression inside the parentheses is a term:

  • Ground terms (concrete values): Fixed values referencing specific entities (typed identities such as User:alice and Document:handbook) or scalar literals ("read", 42, true).
  • Variable terms (placeholders): Lowercase identifiers (user, action, document) that match concrete values during evaluation; see Variables and matching.

A fact is a fully bound predicate statement that records something true in your domain. In .vd syntax, write a fact as a concrete statement with no if body, ending with a semicolon:

is_owner(User:alice, Document:handbook);

Rather than storing a separate permission record for every action a user can take, write rules that derive access from your underlying facts. A rule has a head (the conclusion before if) and a body (the conditions after if):

allow(user: User, "read", document: Document) if
is_owner(user, document);

Verdict derives rule conclusions on demand during evaluation. Because permissions are computed from the underlying facts when needed, your application records is_owner once, and read access updates automatically whenever ownership changes.

In Verdict policies, allow(actor, action, resource) is the standard entry point for authorization checks. Other predicates represent domain relationships, resource attributes, or reusable helpers.

A declare declaration documents the expected term types of an input predicate for readers, editor tooling, and verdict validate:

// Declares the expected shape (no data inserted)
declare is_owner(User, Document);
// Asserts a concrete fact for Alice and the handbook
is_owner(User:alice, Document:handbook);

Declaring your input facts lets verdict validate catch typos in predicate names and arity mismatches across your rules before you publish. See the Language Reference for all supported declaration slot forms.

Putting declarations, facts, and rules together

Section titled “Putting declarations, facts, and rules together”

Combining a declaration, a concrete fact, and a rule in a .vd file produces a complete, self-contained policy model:

declare is_owner(User, Document);
is_owner(User:alice, Document:handbook);
allow(user: User, "read", document: Document) if
is_owner(user, document);

Depending on where a predicate appears, the same syntax performs four roles:

ConstructExampleRole
Factis_owner(User:alice, Document:handbook);Asserts concrete ground truth
Rule headallow(user: User, "read", doc: Document)Defines a conclusion derived when the body succeeds
Rule condition... if is_owner(user, doc);Requires the statement to hold from a fact or rule
Fact type declarationdeclare is_owner(User, Document);Documents expected term types for authors and tooling
Query goalallow(User:alice, "read", doc: Document)Checks a concrete decision or finds matching values

When you evaluate the policy, you pass a predicate expression as a query goal:

// Ground query (concrete check): Is Alice allowed to read the handbook? -> true
allow(User:alice, "read", Document:handbook)
// Open query (seeking bindings): Which documents can Alice read? -> { doc = Document:handbook }
allow(User:alice, "read", doc: Document)

Writing concrete facts directly in a .vd file is useful for self-contained examples and fixed policy constants. In a live application, your backend supplies changing application facts at runtime through the client SDK or REST API; see Managing Facts.