<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DVARA</title>
    <description>The latest articles on DEV Community by DVARA (@dvarahq).</description>
    <link>https://dev.to/dvarahq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3995849%2Ff9318089-ab1a-4327-9a9a-3e825e396885.png</url>
      <title>DEV Community: DVARA</title>
      <link>https://dev.to/dvarahq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dvarahq"/>
    <language>en</language>
    <item>
      <title>LLM Policy as Code: Version-Controlled Governance for Model and Agent Access</title>
      <dc:creator>DVARA</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:32:44 +0000</pubDate>
      <link>https://dev.to/dvarahq/llm-policy-as-code-version-controlled-governance-for-model-and-agent-access-50fl</link>
      <guid>https://dev.to/dvarahq/llm-policy-as-code-version-controlled-governance-for-model-and-agent-access-50fl</guid>
      <description>&lt;p&gt;Ask a team "which models is your application allowed to call, and under what conditions?" and the honest answer is usually &lt;em&gt;"let me check the code."&lt;/em&gt; The rules — which models are approved, which tools an agent may invoke, what happens when a request is too large or comes from the wrong region — are scattered across &lt;code&gt;if&lt;/code&gt; statements in a dozen services. No one can review them in one place, no one can test a change safely, and no one can say what a rule &lt;em&gt;would have done&lt;/em&gt; before it ships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM policy as code&lt;/strong&gt; fixes that the same way infrastructure as code fixed server configuration: move the rules out of application code and into a declarative, version-controlled language that a single control point enforces on every call.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LLM policy as code?
&lt;/h2&gt;

&lt;p&gt;LLM policy as code is the practice of expressing your governance rules — who can call which models and tools, under which conditions — in a declarative, version-controlled format, and enforcing them centrally at a gateway on every LLM and agent (MCP) request, instead of hard-coding them in each application.&lt;/p&gt;

&lt;p&gt;It's the same idea behind Terraform for infrastructure, Open Policy Agent for authorization, and Kubernetes admission control for clusters: the policy is a reviewable artifact, not tribal knowledge. The difference is the domain — an LLM policy matches on &lt;em&gt;AI-native&lt;/em&gt; attributes (model, token budget, requested tools, data-residency region) that a general-purpose API gateway or IAM system can't see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why governance in application code fails
&lt;/h2&gt;

&lt;p&gt;Rules embedded in app code have four problems, and they compound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You can't review them.&lt;/strong&gt; "What's allowed?" has no single answer — you'd have to read every repo that calls a model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can't test them.&lt;/strong&gt; Changing a rule means a deploy and hoping nothing breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can't roll them back.&lt;/strong&gt; A bad rule is another deploy to undo, under pressure, with no record of the previous state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can't audit them.&lt;/strong&gt; There's no trail of who changed which rule, when, or what decision it produced.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Policy as code turns all four from "read the source" into "read the policy" — a diffable, testable, reversible, auditable object.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an LLM policy looks like
&lt;/h2&gt;

&lt;p&gt;A policy is a small YAML document: a set of &lt;strong&gt;rules&lt;/strong&gt;, each with an &lt;code&gt;id&lt;/code&gt;, a &lt;code&gt;priority&lt;/code&gt; (lower number wins), a &lt;code&gt;conditions&lt;/code&gt; block that matches request attributes, and exactly one &lt;code&gt;action&lt;/code&gt;. Here's a model allowlist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;only-approved-models&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;allowlist&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;gpt-4o&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;claude-sonnet-4-5&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DENY&lt;/span&gt;
    &lt;span class="na"&gt;deny_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;on&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;approved&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules evaluate in priority order. The first matching &lt;code&gt;DENY&lt;/code&gt; returns immediately; a softer &lt;code&gt;WARN_AGENT&lt;/code&gt; action collects a warning and lets the call continue.&lt;/p&gt;

&lt;p&gt;The conditions you can match on are the ones that matter for AI traffic: &lt;strong&gt;&lt;code&gt;model&lt;/code&gt;&lt;/strong&gt; (allow/denylist), &lt;strong&gt;&lt;code&gt;max_tokens&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;tools&lt;/code&gt;&lt;/strong&gt; and MCP server/tool/args, &lt;strong&gt;&lt;code&gt;data_residency&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;time_of_day&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;budget&lt;/code&gt; utilization&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the simple form isn't enough
&lt;/h3&gt;

&lt;p&gt;For rules that need &lt;strong&gt;OR&lt;/strong&gt;, &lt;strong&gt;NOT&lt;/strong&gt;, or arithmetic across fields, a rule can carry a CEL (Common Expression Language) &lt;code&gt;expression:&lt;/code&gt; instead of &lt;code&gt;conditions:&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt4o-outside-eu-or-large-requests&lt;/span&gt;
    &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;request.model == "gpt-4o" &amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="s"&gt;(context.region != "EU" || request.message_count &amp;gt;= 50)&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DENY&lt;/span&gt;
    &lt;span class="na"&gt;deny_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;restricted&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;outside&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;EU&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;50&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;messages"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CEL is the same sandboxed expression language Kubernetes admission policies, Envoy, and Cilium use — deterministic and type-checked at submit time. One expression replaces three rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lifecycle is what makes it safe to change
&lt;/h2&gt;

&lt;p&gt;Writing rules as code is only half the value; the other half is changing them without fear:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Author&lt;/strong&gt; — the policy starts as a &lt;strong&gt;Draft&lt;/strong&gt;, affecting nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run&lt;/strong&gt; — simulate it against a real request and see the exact decision before it touches live traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadow&lt;/strong&gt; — run the candidate in production &lt;em&gt;in parallel&lt;/em&gt;, recording what it &lt;em&gt;would&lt;/em&gt; have done with divergence stats, while changing nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Promote&lt;/strong&gt; — activate it; conflict detection surfaces contradictory rules, and it hot-reloads across the fleet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version &amp;amp; roll back&lt;/strong&gt; — every change snapshots a version; revert instantly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Shadow mode is the part teams underrate — the difference between "we think this rule is safe" and "we watched it run against a week of real traffic."&lt;/p&gt;

&lt;h2&gt;
  
  
  Global and tenant scope, audited on every call
&lt;/h2&gt;

&lt;p&gt;Policies apply platform-wide and per-tenant; both are evaluated on every request, and the decision — allowed, denied, or warned, and by which rule — is recorded on every LLM and MCP call. That record is what turns "we have policies" into "here's proof of what our policies did."&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it runs
&lt;/h2&gt;

&lt;p&gt;The rules are enforced at the gateway in front of your model and tool calls — one control point every request already passes through, with full visibility into the AI semantics (model, tokens, tools, region, budget). A denied request never leaves your perimeter.&lt;/p&gt;

&lt;p&gt;That's the core of DVARA's approach: &lt;strong&gt;&lt;a href="https://dvarahq.com/policy-as-code-llm" rel="noopener noreferrer"&gt;Policy-as-Code for LLM and agent traffic&lt;/a&gt;&lt;/strong&gt; as a first-class, version-controlled surface — author it in YAML, dry-run and shadow-test it, promote with conflict detection, roll back in one click. Read the full pillar guide → &lt;strong&gt;&lt;a href="https://dvarahq.com/policy-as-code-llm" rel="noopener noreferrer"&gt;https://dvarahq.com/policy-as-code-llm&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>llm</category>
      <category>governance</category>
    </item>
  </channel>
</rss>
