The Rego policy language is a declarative language used by Open Policy Agent to express authorization and compliance rules as code, evaluated against structured JSON input to produce policy decisions. Instead of scattering if checks for who-can-do-what across your services, you write the rules once in Rego and let a single engine answer the question "is this allowed?" for API requests, Kubernetes admission, CI gates, and infrastructure changes. It is a policy language purpose-built for one job: turning a pile of JSON facts into a decision.
Rego looks unusual at first if you come from imperative programming. Once the mental model clicks, it is compact and predictable.
Why a dedicated policy language exists
You can enforce policy in application code, and plenty of teams do. The problem is that the logic ends up duplicated, inconsistent, and buried where auditors and security engineers cannot easily review it. A separate policy language, decoupled from the application, gives you a single place to define and test rules, and a single engine to evaluate them.
Open Policy Agent (OPA) is the engine. It is a general-purpose decision engine that takes two inputs: the input document (the thing being evaluated, like an HTTP request or a Kubernetes manifest) and optional data (reference information like role mappings). Rego is the language you write the policies in. OPA evaluates the Rego against the inputs and returns a result, usually a boolean or a set of violation messages.
Reading your first Rego rule
Here is a minimal policy that allows a request only if the method is GET:
package httpauth
default allow := false
allow if {
input.method == "GET"
}
Three things are happening. package namespaces the rules. default allow := false sets a safe fallback so that anything not explicitly allowed is denied. The allow if { ... } block defines the condition under which allow becomes true. Every expression inside the braces is implicitly ANDed together, and all of them must hold.
The default-deny pattern is the single most important habit in Rego. Policy should fail closed. If you forget the default line, a rule that never matches produces undefined rather than false, and downstream code that treats undefined as permissive becomes a security hole.
Working with real inputs
Rego shines when the input is nested. Suppose you are gating access to a document based on user role and ownership:
package docs
default allow := false
allow if {
input.user.role == "admin"
}
allow if {
input.action == "read"
input.resource.owner == input.user.id
}
Multiple allow blocks with the same name form a logical OR: the request is allowed if any block succeeds. An admin gets through on the first rule; a regular user reading their own document gets through on the second. Anyone else falls to the default deny.
Iteration uses a distinctive construct. To check whether a user has any of the required groups, you range over a collection:
allow if {
some group in input.user.groups
group == "security-reviewers"
}
The some ... in form introduces a variable and asks whether the condition holds for at least one element. This is how Rego handles "does any item match" without an explicit loop.
Producing violation messages, not just booleans
For admission control and compliance scanning, a bare true/false is not enough. You want to know which rules failed and why. The idiomatic pattern is a partial set of deny messages:
package kubernetes.admission
deny contains msg if {
input.request.kind.kind == "Pod"
some container in input.request.object.spec.containers
container.securityContext.privileged == true
msg := sprintf("Container %s runs privileged", [container.name])
}
deny contains msg if {
input.request.object.spec.hostNetwork == true
msg := "Pod requests host network access"
}
Each rule that matches adds a string to the deny set. The caller collects the set: if it is empty, the resource passes; if not, every message explains a specific problem. This is how tools like OPA Gatekeeper and Conftest report policy failures in Kubernetes and CI.
Where Rego fits in a security program
Policy-as-code with Rego shows up in several places:
- Kubernetes admission control, rejecting workloads that violate baseline security (no privileged containers, required labels, approved registries only).
- CI/CD gates, evaluating Terraform plans or SBOMs before a deploy proceeds.
- API authorization, where a service asks OPA "can this user do this?" on every request.
- Compliance checks, encoding controls from frameworks like SOC 2 or CIS benchmarks as testable rules.
That last use case connects policy-as-code to supply chain security. You can express a rule such as "no dependency with a critical CVE may ship" as Rego evaluated against scan output. A platform like Safeguard can feed structured findings into a policy gate so the decision is enforced automatically rather than remembered by a human. The mechanics of scanning that produce those inputs are covered on the DAST product page, and there is a broader introduction in our academy.
Testing Rego before you trust it
Rego has a built-in test framework, and using it is not optional for production policy. Tests live in files with rules prefixed test_:
package docs_test
import data.docs
test_admin_allowed if {
docs.allow with input as {"user": {"role": "admin"}}
}
test_stranger_denied if {
not docs.allow with input as {"user": {"role": "guest"}, "action": "read"}
}
Run them with opa test .. The with input as construct injects a mock input, which lets you assert both the allow and the deny paths. A policy without tests for its deny paths is a policy that will eventually fail open in a way nobody notices until an incident.
FAQ
Is Rego hard to learn?
The syntax is small, but the declarative model takes adjustment if you are used to imperative code. The two ideas that unlock it are default-deny (always set default allow := false) and the fact that expressions in a rule body are ANDed while multiple rules of the same name are ORed. Once those land, most policies are short.
What is the difference between Rego and OPA?
OPA (Open Policy Agent) is the decision engine that evaluates policies and returns results. Rego is the language you write those policies in. You author Rego; OPA runs it against your input and data documents.
Can Rego enforce security policy in CI/CD pipelines?
Yes. Tools like Conftest run Rego against structured files such as Terraform plans, Kubernetes manifests, or SBOMs, and fail the build when a deny rule matches. This is a common way to enforce guardrails before infrastructure or dependencies reach production.
How do I test a Rego policy?
Write test rules prefixed with test_ that use with input as to supply mock data, then run opa test. Cover both the paths that should allow and the paths that should deny, since untested deny logic is where policies silently break.