<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Opa on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/opa/</link><description>Recent content in Opa on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 09 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/opa/index.xml" rel="self" type="application/rss+xml"/><item><title>Policy as Code — Overview</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/overview/</link><pubDate>Tue, 09 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/overview/</guid><description>&lt;p&gt;Encoding compliance, security, and operational rules as version-controlled, testable code — evaluated at the point where things are created or changed rather than audited after the fact.&lt;/p&gt;
&lt;p&gt;The alternative is manual review and reactive alerts. Policy as code shifts that left: rules are explicit, reviewable, and enforced automatically.&lt;/p&gt;
&lt;h2 id="enforcement-points"&gt;Enforcement points
&lt;/h2&gt;&lt;p&gt;The same policy intent can be applied at three different points in the lifecycle:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CI/CD gate&lt;/strong&gt; — evaluate before anything reaches the cluster. Fail the pipeline on violations. Tools: Conftest, Checkov, Trivy IaC. Lowest blast radius, fastest feedback loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cluster admission&lt;/strong&gt; — intercept every &lt;code&gt;kubectl apply&lt;/code&gt; at the API server. No non-compliant resource can be created or updated. Tools: Kyverno, Gatekeeper (OPA), K8s-native ValidatingAdmissionPolicy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runtime / drift detection&lt;/strong&gt; — continuously evaluate live state against policy and surface violations. Catches things that predate the policy, or that arrived outside normal pipelines. Tools: OPA, AWS Config, Kyverno audit mode.&lt;/p&gt;
&lt;p&gt;Most teams layer these: CI catches the obvious mistakes early and cheap, admission control enforces hard requirements at the cluster boundary, audit mode gives visibility into what is already out of compliance.&lt;/p&gt;
&lt;h2 id="how-the-tools-relate"&gt;How the tools relate
&lt;/h2&gt;&lt;p&gt;OPA is the common engine underneath Gatekeeper and Conftest. Rego is its policy language — learn it once and it applies across both. Kyverno is a separate engine that replaces Rego with Kubernetes-native YAML; lower barrier to entry, but the policy language does not transfer outside K8s. The K8s-native ValidatingAdmissionPolicy uses CEL, a simpler expression language built into the API server — no extra install, but limited to validation and basic mutation.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Policy intent
 ├── CI/CD gate → Conftest (Rego) / Checkov / Trivy IaC
 ├── K8s admission → Kyverno (YAML) / Gatekeeper (Rego) / VAP (CEL)
 └── Runtime → OPA server / Kyverno audit / AWS Config
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="to-explore"&gt;To explore
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rego standalone&lt;/strong&gt; — eval policies against arbitrary JSON input using &lt;code&gt;opa eval&lt;/code&gt; and the REPL, without Kubernetes. Good way to learn the language in isolation before wiring it into a pipeline or cluster. See the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/opa/" &gt;OPA &amp;amp; Rego&lt;/a&gt; note.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Conftest</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/conftest/</link><pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/conftest/</guid><description>&lt;p&gt;Conftest is a CLI tool that runs OPA policies against structured config files — Kubernetes manifests, Terraform plans, Helm output, Dockerfiles, GitHub Actions workflows, anything that can be parsed. It is the CI/CD enforcement layer on top of Rego.&lt;/p&gt;
&lt;p&gt;Write a policy once in Rego, run &lt;code&gt;conftest test&lt;/code&gt; in your pipeline, fail the build if violations are found.&lt;/p&gt;
&lt;h2 id="install"&gt;Install
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install conftest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="basic-usage"&gt;Basic usage
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Test a Kubernetes manifest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conftest test deployment.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Test all YAML in a directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conftest test k8s/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Test a Terraform plan (JSON output)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;terraform show -json tfplan &amp;gt; tfplan.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conftest test tfplan.json --parser json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Test Helm-rendered output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm template my-app ./chart | conftest test -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default Conftest looks for policies in &lt;code&gt;./policy/&lt;/code&gt;. Change with &lt;code&gt;--policy&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="policy-structure"&gt;Policy structure
&lt;/h2&gt;&lt;p&gt;Policies are Rego files in the &lt;code&gt;policy/&lt;/code&gt; directory. Conftest checks for &lt;code&gt;deny&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, and &lt;code&gt;violation&lt;/code&gt; rules.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rego" data-lang="rego"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# policy/k8s.rego&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# deny blocks the pipeline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;deny &lt;span style="color:#66d9ef"&gt;contains&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Deployment&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;securityContext&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;runAsNonRoot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Deployment %v must set runAsNonRoot&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; [&lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# warn prints a warning but does not fail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;warn &lt;span style="color:#66d9ef"&gt;contains&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Deployment&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;labels&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;app.kubernetes.io/version&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Deployment %v is missing version label&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; [&lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;$ conftest test deployment.yaml
FAIL - deployment.yaml - main - Deployment nginx must set runAsNonRoot
WARN - deployment.yaml - main - Deployment nginx is missing version label

2 tests, 0 passed, 1 warning, 1 failure
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="namespaces"&gt;Namespaces
&lt;/h2&gt;&lt;p&gt;Use &lt;code&gt;--namespace&lt;/code&gt; to scope which Rego package Conftest evaluates. Useful when you have per-resource-type policies in separate packages.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conftest test deployment.yaml --namespace kubernetes.deployments
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="multiple-parsers"&gt;Multiple parsers
&lt;/h2&gt;&lt;p&gt;Conftest supports many input formats. Common ones:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Flag&lt;/th&gt;
 &lt;th&gt;Parses&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--parser yaml&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;YAML (default for &lt;code&gt;.yaml&lt;/code&gt;/&lt;code&gt;.yml&lt;/code&gt;)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--parser json&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;JSON, Terraform plan&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--parser hcl2&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Terraform HCL&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--parser dockerfile&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Dockerfiles&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--parser toml&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;TOML&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="sharing-policies"&gt;Sharing policies
&lt;/h2&gt;&lt;p&gt;Policies can be distributed as OCI artifacts and pulled with &lt;code&gt;conftest pull&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conftest pull ghcr.io/myorg/policies:latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conftest test deployment.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;a class="link" href="https://www.conftest.dev/sharing/" target="_blank" rel="noopener"
 &gt;Conftest policy hub&lt;/a&gt; documents the format. Useful for sharing a common policy library across repos without copy-pasting Rego.&lt;/p&gt;
&lt;h2 id="in-ci"&gt;In CI
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# GitHub Actions example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Policy check&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; conftest test k8s/ --policy policy/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Exits non-zero on &lt;code&gt;deny&lt;/code&gt; violations. &lt;code&gt;warn&lt;/code&gt; violations print but do not fail the build — useful for phased enforcement (warn first, deny later).&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.conftest.dev/" target="_blank" rel="noopener"
 &gt;Conftest documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/open-policy-agent/conftest" target="_blank" rel="noopener"
 &gt;Conftest GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/open-policy-agent/conftest/tree/master/examples" target="_blank" rel="noopener"
 &gt;Example policies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Kubernetes Policy</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/kubernetes-policy/</link><pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/kubernetes-policy/</guid><description>&lt;p&gt;Kubernetes has three distinct policy enforcement mechanisms. They sit at the same point in the request lifecycle — the admission controller — but differ in language, capability, and operational complexity.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Kyverno&lt;/th&gt;
 &lt;th&gt;Gatekeeper (OPA)&lt;/th&gt;
 &lt;th&gt;ValidatingAdmissionPolicy&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Language&lt;/td&gt;
 &lt;td&gt;YAML/JMESPath&lt;/td&gt;
 &lt;td&gt;Rego&lt;/td&gt;
 &lt;td&gt;CEL&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Native to K8s&lt;/td&gt;
 &lt;td&gt;No (CRD)&lt;/td&gt;
 &lt;td&gt;No (CRD)&lt;/td&gt;
 &lt;td&gt;Yes (built-in)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Validate&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Mutate&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;Limited&lt;/td&gt;
 &lt;td&gt;Yes (1.32+)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Generate&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Image verify&lt;/td&gt;
 &lt;td&gt;Yes&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;td&gt;No&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GA since&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;—&lt;/td&gt;
 &lt;td&gt;K8s 1.30&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Good for&lt;/td&gt;
 &lt;td&gt;Full-featured, K8s-native feel&lt;/td&gt;
 &lt;td&gt;Rego-first teams, policy-as-data&lt;/td&gt;
 &lt;td&gt;Simple rules, no extra install&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="validatingadmissionpolicy-vap"&gt;ValidatingAdmissionPolicy (VAP)
&lt;/h2&gt;&lt;p&gt;Added in Kubernetes 1.26, &lt;strong&gt;GA in 1.30&lt;/strong&gt;. Policies are built into the API server — no admission controller to deploy or maintain. Policy is written in &lt;strong&gt;CEL&lt;/strong&gt; (Common Expression Language), a simple expression language also used in Kubernetes&amp;rsquo; &lt;code&gt;x-kubernetes-validations&lt;/code&gt; CRD validation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;admissionregistration.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ValidatingAdmissionPolicy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;require-run-as-non-root&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;failurePolicy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Fail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchConstraints&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resourceRules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;apiGroups&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;apps&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;apiVersions&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;v1&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;operations&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;CREATE&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;UPDATE&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resources&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;deployments&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;expression&lt;/span&gt;: &amp;gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; object.spec.template.spec.securityContext.runAsNonRoot == true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;message&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Pods must run as non-root&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;admissionregistration.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ValidatingAdmissionPolicyBinding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;require-run-as-non-root-binding&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;policyName&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;require-run-as-non-root&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validationActions&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;Deny]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchResources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespaceSelector&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;enforce-policy&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;ValidatingAdmissionPolicyBinding&lt;/code&gt; scopes where a policy applies — cluster-wide, specific namespaces, or by label selector.&lt;/p&gt;
&lt;h3 id="cel-basics"&gt;CEL basics
&lt;/h3&gt;&lt;p&gt;CEL expressions have access to &lt;code&gt;object&lt;/code&gt; (the incoming resource), &lt;code&gt;oldObject&lt;/code&gt; (for updates), &lt;code&gt;request&lt;/code&gt; (metadata, user, etc.), and &lt;code&gt;params&lt;/code&gt; (a referenced ConfigMap or CRD for parameterisation).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Simple field check
object.spec.replicas &amp;lt;= 10

# Nested optional field (use ?. for optional traversal)
object.spec.template.spec.?securityContext.?runAsNonRoot == optional.of(true)

# List comprehension — all containers must have limits
object.spec.template.spec.containers.all(c,
 has(c.resources) &amp;amp;&amp;amp; has(c.resources.limits)
)

# String operations
object.metadata.name.startsWith(&amp;#34;prod-&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="mutatingadmissionpolicy"&gt;MutatingAdmissionPolicy
&lt;/h3&gt;&lt;p&gt;Added in Kubernetes 1.32 (alpha). Brings CEL-based mutation — set defaults, inject labels, patch fields — without Kyverno or a webhook. Still early; not production-ready yet.&lt;/p&gt;
&lt;h2 id="gatekeeper"&gt;Gatekeeper
&lt;/h2&gt;&lt;p&gt;OPA running as a Kubernetes admission controller. Policies are written in Rego and stored as &lt;code&gt;ConstraintTemplate&lt;/code&gt; CRDs. The separation between template (the Rego logic) and constraint (the enforcement configuration + parameters) is the key design pattern.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;templates.gatekeeper.sh/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ConstraintTemplate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;k8srequiredlabels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;crd&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;names&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;K8sRequiredLabels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validation&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;openAPIV3Schema&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;properties&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;array&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;items&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;string}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;targets&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;target&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;admission.k8s.gatekeeper.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rego&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; package k8srequiredlabels
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; violation[{&amp;#34;msg&amp;#34;: msg}] {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; provided := {label | input.review.object.metadata.labels[label]}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; required := {label | label := input.parameters.labels[_]}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; missing := required - provided
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; count(missing) &amp;gt; 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; msg := sprintf(&amp;#34;missing required labels: %v&amp;#34;, [missing])
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;constraints.gatekeeper.sh/v1beta1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;K8sRequiredLabels&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;require-team-label&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kinds&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;apiGroups&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;kinds&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Namespace&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;parameters&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;labels&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;team&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;environment&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Gatekeeper also supports &lt;strong&gt;audit mode&lt;/strong&gt; — it continuously evaluates existing resources against policies and surfaces violations without blocking. Useful for measuring compliance against policies you&amp;rsquo;re not yet ready to enforce.&lt;/p&gt;
&lt;h3 id="gatekeeper-vs-kyverno"&gt;Gatekeeper vs Kyverno
&lt;/h3&gt;&lt;p&gt;Kyverno is better if your team does not know Rego and wants policies that look like Kubernetes manifests. Gatekeeper is better if you are already invested in OPA/Rego and want a single policy language across K8s and non-K8s surfaces (via Conftest).&lt;/p&gt;
&lt;p&gt;The K8s-native VAP is the right default for simple validation rules on new clusters — no extra install, but it does not cover mutation (until 1.32+), generation, or image verification.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://open-policy-agent.github.io/gatekeeper/" target="_blank" rel="noopener"
 &gt;Gatekeeper documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/open-policy-agent/gatekeeper-library" target="_blank" rel="noopener"
 &gt;Gatekeeper library&lt;/a&gt; — ready-made ConstraintTemplates&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/" target="_blank" rel="noopener"
 &gt;ValidatingAdmissionPolicy docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/docs/reference/using-api/cel/" target="_blank" rel="noopener"
 &gt;CEL in Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/kyverno/" &gt;Kyverno&lt;/a&gt; — separate note&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>OPA &amp; Rego</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/opa/</link><pubDate>Mon, 08 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/policy-as-code/opa/</guid><description>&lt;p&gt;OPA (Open Policy Agent) is a general-purpose policy engine. It takes structured input (JSON), evaluates it against a policy written in Rego, and returns a decision. It does not care what the input represents — Kubernetes resources, Terraform plans, HTTP requests, CI artifacts — anything you can express as JSON.&lt;/p&gt;
&lt;p&gt;Rego is the query language OPA uses to express policy. It is declarative and logic-based (Datalog-inspired), which is a different mental model from imperative code. The learning curve is real but the payoff is policies that are testable, composable, and auditable.&lt;/p&gt;
&lt;h2 id="install"&gt;Install
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install opa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or download a binary from &lt;a class="link" href="https://github.com/open-policy-agent/opa/releases" target="_blank" rel="noopener"
 &gt;github.com/open-policy-agent/opa/releases&lt;/a&gt;. No dependencies.&lt;/p&gt;
&lt;h2 id="standalone-eval"&gt;Standalone eval
&lt;/h2&gt;&lt;p&gt;The fastest way to learn Rego is the REPL and the &lt;code&gt;eval&lt;/code&gt; command — no Kubernetes, no Conftest, just OPA and a policy file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Interactive REPL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Eval a query against a policy file and input&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa eval &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --data policy.rego &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --input input.json &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;data.mypackage.allow&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Run tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa test ./policies/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Run tests with coverage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa test --coverage ./policies/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Check syntax without evaluating&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa check policy.rego
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="repl-basics"&gt;REPL basics
&lt;/h3&gt;&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; data # inspect all loaded data
&amp;gt; input # inspect current input
&amp;gt; x := {&amp;#34;a&amp;#34;: 1}; x.a == 1 # inline assignment and check
&amp;gt; :help
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="rego-basics"&gt;Rego basics
&lt;/h2&gt;&lt;p&gt;A policy file is a package with rules. Rules are boolean expressions — they are &lt;code&gt;true&lt;/code&gt; when all conditions in the body hold.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rego" data-lang="rego"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mypackage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# default value when no rule fires&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;allow&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# allow if both conditions hold&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;allow &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;action&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;read&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="sets-and-comprehensions"&gt;Sets and comprehensions
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rego" data-lang="rego"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mypackage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# set of violations — empty set means compliant&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;violations &lt;span style="color:#66d9ef"&gt;contains&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;containers&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;image&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;latest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;image tag must not be latest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# all images&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;images &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; {&lt;span style="color:#a6e22e"&gt;img&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;img&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;containers&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;]&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;image&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="iteration"&gt;Iteration
&lt;/h3&gt;&lt;p&gt;Rego iterates implicitly. &lt;code&gt;input.containers[_]&lt;/code&gt; means &amp;ldquo;for any element in containers&amp;rdquo;. Use &lt;code&gt;[i]&lt;/code&gt; if you need the index.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rego" data-lang="rego"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# any container missing a CPU limit is a violation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;violations &lt;span style="color:#66d9ef"&gt;contains&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;container&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;containers&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;container&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;resources&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;limits&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;cpu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;msg&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;container %v has no CPU limit&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; [&lt;span style="color:#a6e22e"&gt;container&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="testing"&gt;Testing
&lt;/h3&gt;&lt;p&gt;OPA has a built-in test runner. Test files are Rego files with rules prefixed &lt;code&gt;test_&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-rego" data-lang="rego"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mypackage_test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mypackage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test_allow_admin &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;mypackage&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;allow&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;action&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;read&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test_deny_non_admin &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;not&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mypackage&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;allow&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;input&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;bob&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;action&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;read&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa test ./
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# PASS: 2/2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="input-and-data"&gt;Input and data
&lt;/h2&gt;&lt;p&gt;OPA separates &lt;strong&gt;input&lt;/strong&gt; (the thing being evaluated — changes per request) from &lt;strong&gt;data&lt;/strong&gt; (context that does not change — lookup tables, allowlists, base policy).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# data is loaded with --data, can be JSON or Rego&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa eval --data policy.rego --data allowlist.json --input request.json &lt;span style="color:#e6db74"&gt;&amp;#34;data.authz.allow&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="opa-as-a-server"&gt;OPA as a server
&lt;/h2&gt;&lt;p&gt;OPA can run as a sidecar or standalone HTTP server. POST JSON to &lt;code&gt;/v1/data/&amp;lt;package&amp;gt;/&amp;lt;rule&amp;gt;&lt;/code&gt; to get a decision.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opa run --server --addr :8181 policy.rego
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST http://localhost:8181/v1/data/mypackage/allow &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -d &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;input&amp;#34;: {&amp;#34;user&amp;#34;: &amp;#34;admin&amp;#34;, &amp;#34;action&amp;#34;: &amp;#34;read&amp;#34;}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# {&amp;#34;result&amp;#34;: true}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is how Gatekeeper and Conftest use OPA under the hood.&lt;/p&gt;
&lt;h2 id="rego-playground"&gt;Rego playground
&lt;/h2&gt;&lt;p&gt;The &lt;a class="link" href="https://play.openpolicyagent.org/" target="_blank" rel="noopener"
 &gt;Rego Playground&lt;/a&gt; is the fastest way to experiment without installing anything. Paste a policy and input, evaluate, iterate.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.openpolicyagent.org/docs/latest/" target="_blank" rel="noopener"
 &gt;OPA documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://play.openpolicyagent.org/" target="_blank" rel="noopener"
 &gt;Rego Playground&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.openpolicyagent.org/docs/latest/policy-cheatsheet/" target="_blank" rel="noopener"
 &gt;Rego cheat sheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/open-policy-agent/opa" target="_blank" rel="noopener"
 &gt;OPA GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://academy.styra.com/" target="_blank" rel="noopener"
 &gt;Styra Academy&lt;/a&gt; — free Rego courses (Styra built OPA)&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>