Deny/allow patterns
The idioms real policies use.
Real-world Rego settles on a few idioms. The most common is the deny set: a rule deny contains msg that accumulates a message for every violation found, so evaluating deny yields the full list of problems (empty means compliant). This is what Conftest and Gatekeeper expect. It reads naturally — “add a denial message when this bad condition holds” — and iterating with [_] lets one rule check every element.
package maindeny contains msg if {input.kind == "Service"input.spec.type == "LoadBalancer"not input.metadata.annotations["acme.io/public-approved"]msg := sprintf("Service %v is a public LoadBalancer without approval", [input.metadata.name])}
allow, default, and full-decision policies
For authorization you often want the opposite shape: default allow := false, then allow if { ... } rules that grant access — deny-by-default, allow explicitly. And beyond boolean, a policy can return a structured decision (an object with allowed, reasons, and remediation) so the caller gets rich feedback. Choosing the shape — deny-set for validation, allow-with-default for authz, structured for detailed responses — is most of designing a policy.
package authzdefault allow := false # deny by defaultallow if {input.user.roles[_] == "editor"input.action == "write"startswith(input.resource, "docs/")}