Rego basics: rules & queries

How Rego actually evaluates.

Advanced14 min · lesson 3 of 12

Rego is OPA’s declarative language, and it thinks differently from imperative code. A rule defines the conditions under which something is true; OPA finds all the ways to satisfy it. Expressions in a rule body are implicitly ANDed, and a rule with the same name defined multiple times is implicitly ORed. Variables are not assignments so much as things Rego solves for, and iteration happens by unification rather than explicit loops.

policy.rego
package example
# allow is true if ALL body lines hold (implicit AND)
allow if {
input.method == "GET"
input.path == "/health"
}
# a second allow rule = OR: allow is also true if this holds
allow if {
input.user.role == "admin"
}

Iteration and the underscore

Rego iterates by referencing a collection with a variable (or _ for “any”): input.servers[_].port checks the port of every server, and the rule succeeds if any iteration satisfies the body. Comprehensions build new collections ([p | p := input.ports[_]; p > 1024]). This takes adjusting to — you are describing what must be true, and OPA searches for satisfying values — but it makes policies compact once it clicks.

policy.rego
package example
# deny if ANY container runs as privileged (iterate with [_])
deny contains msg if {
c := input.spec.containers[_]
c.securityContext.privileged == true
msg := sprintf("container %v is privileged", [c.name])
}
Undefined is not false — and that is a common bug
In Rego, a reference to a missing field (input.foo.bar when foo is absent) is undefined, and an undefined expression makes the rule body fail for that case rather than raising an error. This means a policy can silently pass because a field it checks does not exist. Guard with existence checks or default values, and test with inputs that omit fields, so “undefined” never masquerades as “compliant.”