<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>DVARA Blog</title>
        <link>https://dvarahq.com/blog</link>
        <description>DVARA Blog</description>
        <lastBuildDate>Sat, 24 Oct 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[DVARA 1.11 and 1.12 — The Cost Advisor, current Anthropic SDKs, and token counts that match the bill]]></title>
            <link>https://dvarahq.com/blog/dvara-1-12-0</link>
            <guid>https://dvarahq.com/blog/dvara-1-12-0</guid>
            <pubDate>Sat, 24 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DVARA 1.12 adds a Cost Advisor that prices ways to spend less and checks a cheaper model on your traffic first. It also serves the current Anthropic SDKs.]]></description>
            <content:encoded><![CDATA[<p>A gateway sees every model call, so it knows where the money goes. Until now DVARA
used that to enforce budgets and to show spend. It did not tell you how to spend
less.</p>
<p>DVARA 1.12.0 does. The new Cost Advisor reads your last 30 days of calls and
lists ways to spend less, each with what it saves a month. It also checks a
cheaper model on your own traffic before it calls the switch safe. This post
covers 1.11.0 too, which shipped the day before.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ways-to-spend-less-priced-from-your-own-traffic">Ways to spend less, priced from your own traffic<a href="https://dvarahq.com/blog/dvara-1-12-0#ways-to-spend-less-priced-from-your-own-traffic" class="hash-link" aria-label="Direct link to Ways to spend less, priced from your own traffic" title="Direct link to Ways to spend less, priced from your own traffic" translate="no">​</a></h2>
<p><strong>Cost → Cost Advisor</strong> in Flightdeck shows four kinds of suggestion:</p>
<ul>
<li class=""><strong>Use a cheaper model.</strong> When a workspace spends at least $50 a month on a
model, the advisor looks for a model from the same provider that can do the
same things: tools, structured output, reasoning, images and enough context.
It picks the closest step down that costs at most 70% as much, not the
cheapest one.</li>
<li class=""><strong>Turn on the response cache.</strong> If the cache is off and some requests repeat
one it would have answered, the advisor shows what those calls cost. One
click turns the cache on.</li>
<li class=""><strong>Route by tier.</strong> For a model whose traffic is often simple, it prices what
routing by complexity would save, using a tier map you already have.</li>
<li class=""><strong>Large system prompts.</strong> It flags an API key that sends a prompt of 2,000
tokens or more on every call, and what prompt caching would save. DVARA never
changes a prompt.</li>
</ul>
<p>Every figure is priced the way your bill is: on the tokens each call used, at
your prices. A suggestion under $5 a month is not shown. Each one can be
dismissed with a reason, and comes back only if its saving doubles. Every apply
and dismissal goes on the audit trail. See
<a class="" href="https://dvarahq.com/docs/flight-deck/cost-management#cost-advisor">Cost Advisor</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-cheaper-model-is-checked-before-it-is-called-safe">A cheaper model is checked before it is called safe<a href="https://dvarahq.com/blog/dvara-1-12-0#a-cheaper-model-is-checked-before-it-is-called-safe" class="hash-link" aria-label="Direct link to A cheaper model is checked before it is called safe" title="Direct link to A cheaper model is checked before it is called safe" translate="no">​</a></h2>
<p>A cheaper model only saves money if its answers are as good. So a workspace
admin can let DVARA check it on the workspace's own requests.</p>
<p>After an answer has gone back to your application, the Gateway sends a small
sample of requests (2%, at most 200 a day per Gateway) to the cheaper model. The
request goes through the same policies, PII rules and guardrails as any other. A
judge model then compares the two answers. Only the verdict is kept: no text of
the request or either answer is stored.</p>
<p>After 30 checks, the suggestion shows <strong>Validated</strong> when quality held in at
least 90% of them. Otherwise it shows <strong>Quality dropped</strong>, and DVARA stops trying
that model. The checks are billed to the workspace, up to $10 a month unless you
set another limit, and they never change an answer. It is off until the admin
turns it on. See
<a class="" href="https://dvarahq.com/docs/flight-deck/budgets-and-chargeback#check-cheaper-models-on-your-traffic">Check cheaper models on your traffic</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-current-anthropic-sdks-work">The current Anthropic SDKs work<a href="https://dvarahq.com/blog/dvara-1-12-0#the-current-anthropic-sdks-work" class="hash-link" aria-label="Direct link to The current Anthropic SDKs work" title="Direct link to The current Anthropic SDKs work" translate="no">​</a></h2>
<p>Anthropic's new API version, <code>2026-10-21</code>, is what its current SDKs send: Java
3, Python 2, TypeScript 1, Go 2, C# 13, PHP 1, Ruby 2 and the <code>ant</code> CLI 2.
Up to 1.11, DVARA's <code>/v1/messages</code> accepted only <code>2023-06-01</code>, so a client that
upgraded its SDK was refused.</p>
<p>1.12.0 serves both. Under <code>2026-10-21</code>, DVARA refuses what that version removed,
the same way Anthropic does: <code>temperature</code>, <code>top_p</code> and <code>top_k</code>, a document in
an assistant message, and citations on a user's text. Clients on <code>2023-06-01</code>,
including Claude Code, work as before. See
<a class="" href="https://dvarahq.com/docs/llm-gateway#use-the-anthropic-messages-api">Use the Anthropic Messages API</a>.</p>
<p>DVARA's own calls to Anthropic move to <code>2026-10-21</code> as well. When DVARA
translates a request for Anthropic, for example one that came in on
<code>/v1/chat/completions</code>, it now leaves out <code>temperature</code> and <code>top_p</code>, and the
<code>X-Gateway-Params-Not-Applied</code> response header says so. To keep the old
version, set <code>ANTHROPIC_API_VERSION=2023-06-01</code> on the Gateway.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="token-counts-that-match-the-bill">Token counts that match the bill<a href="https://dvarahq.com/blog/dvara-1-12-0#token-counts-that-match-the-bill" class="hash-link" aria-label="Direct link to Token counts that match the bill" title="Direct link to Token counts that match the bill" translate="no">​</a></h2>
<p>DVARA counts tokens itself, for rate limits and for checking a call's cost
before it is sent. In 1.11.0 we measured what Mistral bills. The text count was
already exact. Mistral adds 8 tokens to every request, and 28 more when it has
tools. DVARA now adds the same. You can set your own figure per model family.
See <a class="" href="https://dvarahq.com/docs/open-source/configuration#counting-tokens">Counting tokens</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="smaller-fixes">Smaller fixes<a href="https://dvarahq.com/blog/dvara-1-12-0#smaller-fixes" class="hash-link" aria-label="Direct link to Smaller fixes" title="Direct link to Smaller fixes" translate="no">​</a></h2>
<ul>
<li class="">A streamed answer from Cohere failed on its first event. It works now.</li>
<li class="">The Python MCP SDK 2.3.0 works with DVARA on MCP's stateless revision,
2026-07-28, as a client and as a server behind the Gateway.</li>
<li class="">Two quick edits to the tier catalogue both take effect. Before, the second
could be missed until the file changed again.</li>
<li class="">A Gateway that joins the cluster late now learns the agent sessions that were
killed before it joined.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-this-before-upgrading">Read this before upgrading<a href="https://dvarahq.com/blog/dvara-1-12-0#read-this-before-upgrading" class="hash-link" aria-label="Direct link to Read this before upgrading" title="Direct link to Read this before upgrading" translate="no">​</a></h2>
<p>Nothing you set before stops working. Four things to know:</p>
<ol>
<li class="">Clients on the current Anthropic SDKs now reach <code>/v1/messages</code>. Under
<code>2026-10-21</code>, leave out <code>temperature</code>, <code>top_p</code> and <code>top_k</code>.</li>
<li class="">DVARA calls Anthropic on <code>2026-10-21</code>, so a translated request no longer
sends <code>temperature</code> or <code>top_p</code>. <code>ANTHROPIC_API_VERSION=2023-06-01</code> keeps the
old behaviour.</li>
<li class="">Mistral counts are 8 tokens higher per request, 28 more with tools, as
Mistral bills them.</li>
<li class="">Flightdeck adds one table for the Cost Advisor when it starts. Back up the
database first.</li>
</ol>
<p>The <a class="" href="https://dvarahq.com/docs/releases-and-upgrading#moving-to-1120">upgrade guide</a> has every
change.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-dvara-112">Try DVARA 1.12<a href="https://dvarahq.com/blog/dvara-1-12-0#try-dvara-112" class="hash-link" aria-label="Direct link to Try DVARA 1.12" title="Direct link to Try DVARA 1.12" translate="no">​</a></h2>
<p>The images <code>ghcr.io/dvarahq/dvara-gateway:1.12.0</code> and
<code>ghcr.io/dvarahq/dvara-flightdeck:1.12.0</code> and the Helm chart 1.12.0 are out now.
DVARA Open Source 1.12.0 is out too: get the
<a href="https://github.com/dvarahq/dvara/releases/tag/1.12.0" target="_blank" rel="noopener noreferrer" class="">release</a>, follow the
<a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a>, or
<a class="" href="https://dvarahq.com/book-a-demo">book a walkthrough</a> of the Enterprise platform.</p>]]></content:encoded>
            <category>governance</category>
            <category>open-source</category>
        </item>
        <item>
            <title><![CDATA[DVARA 1.10.0 — See what routing saves, and rules that hold]]></title>
            <link>https://dvarahq.com/blog/dvara-1-10-0</link>
            <guid>https://dvarahq.com/blog/dvara-1-10-0</guid>
            <pubDate>Fri, 23 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DVARA 1.10 explains every routing decision and what it saved, counts tokens and cost before a call, judges live answers through the governed path, and makes rules that cannot check refuse instead of pass.]]></description>
            <content:encoded><![CDATA[<p>A gateway that routes, prices and guards model calls has to show its work. If it
picks a cheaper model, you should see why and what it saved. If a rule cannot
check a request, the request should not slip through.</p>
<p>DVARA 1.10.0 is built around that. Routing explains each decision. Cost and
tokens can be counted before a call. Live answers can be judged. And rules that
are meant to block now block when they cannot check.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="see-what-routing-decided-and-what-it-saved">See what routing decided, and what it saved<a href="https://dvarahq.com/blog/dvara-1-10-0#see-what-routing-decided-and-what-it-saved" class="hash-link" aria-label="Direct link to See what routing decided, and what it saved" title="Direct link to See what routing decided, and what it saved" translate="no">​</a></h2>
<p>Intelligent routing sends each request to the cheapest model tier that can
answer it. In 1.10.0 every routed answer says which tier it got, which model was
asked for, which was chosen, and why. The audit record carries the same.</p>
<p>A route can now name a model for a <strong>reasoning</strong> tier, used when the caller asks
for high reasoning effort. A workspace can set its lowest tier, so it is never
served below it. The Usage page shows what routing saved against the model that
was asked for. When the router itself calls a model to classify a request, that
call goes through your policies, budgets and audit like any other. See
<a class="" href="https://dvarahq.com/docs/routing#intelligent-routing">Intelligent routing</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="count-tokens-and-cost-before-you-call">Count tokens and cost before you call<a href="https://dvarahq.com/blog/dvara-1-10-0#count-tokens-and-cost-before-you-call" class="hash-link" aria-label="Direct link to Count tokens and cost before you call" title="Direct link to Count tokens and cost before you call" translate="no">​</a></h2>
<p><code>POST /v1/tokens/count</code> gives a request's token count, where it would go, how
much of the context window it uses, and what it would cost, for up to ten models,
without calling any of them. Flightdeck has a <strong>Token Calculator</strong> under Cost.
Mistral and DeepSeek models are now counted with their own vocabularies.</p>
<p>Usage and spend are also split by the API a call came in on: Chat Completions,
Responses, Messages, Gemini and the rest. Since each coding agent uses its own
API, that shows what each agent costs. See
<a class="" href="https://dvarahq.com/docs/flight-deck/cost-management">Cost management</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="judge-answers-on-live-traffic">Judge answers on live traffic<a href="https://dvarahq.com/blog/dvara-1-10-0#judge-answers-on-live-traffic" class="hash-link" aria-label="Direct link to Judge answers on live traffic" title="Direct link to Judge answers on live traffic" translate="no">​</a></h2>
<p>DVARA can now score a sample of live answers as they are served: valid JSON
when JSON was asked for, refusals, toxic content, length and patterns you set.
Scoring never slows or fails a request, and every score is a signed audit event.</p>
<p>You can also name a judge model to grade sampled answers for accuracy, tone,
policy adherence or your own rubric. The judge's call goes through the gateway,
so your PII rules and budgets apply to what it is sent. Its spend has its own
monthly limit and is kept out of chargeback. See
<a class="" href="https://dvarahq.com/docs/flight-deck/prompts-and-evals">Prompts and evals</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rules-that-are-meant-to-block-now-block">Rules that are meant to block now block<a href="https://dvarahq.com/blog/dvara-1-10-0#rules-that-are-meant-to-block-now-block" class="hash-link" aria-label="Direct link to Rules that are meant to block now block" title="Direct link to Rules that are meant to block now block" translate="no">​</a></h2>
<p>A rule that cannot check a request used to let it through. In 1.10.0:</p>
<ul>
<li class="">A data-residency rule refuses a call whose region is unknown, and a budget
rule refuses when budget use cannot be read.</li>
<li class="">The ML injection classifier and the PII service can be set to refuse when they
are down, and every time one lets a request through unchecked, it is recorded.</li>
<li class="">A guardrail plugin set to fail closed treats a broken answer as a failure.</li>
<li class="">Failover keeps to a workspace's allowed regions. A workspace can choose to be
served elsewhere instead, and each time it is, that is recorded.</li>
</ul>
<p>DVARA also finds model output that acts when it is shown: terminal escape
sequences, and images or links that would send data to an untrusted host. A model
file you supply can be pinned by its SHA-256. Gateways now talk to each other over
TLS 1.3, and only those holding a certificate from the cluster's own authority
can join. See <a class="" href="https://dvarahq.com/docs/guardrails">Guardrails</a> and
<a class="" href="https://dvarahq.com/docs/ml-and-plugin-guardrails">ML and plugin guardrails</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="change-more-in-flightdeck-with-no-restart">Change more in Flightdeck, with no restart<a href="https://dvarahq.com/blog/dvara-1-10-0#change-more-in-flightdeck-with-no-restart" class="hash-link" aria-label="Direct link to Change more in Flightdeck, with no restart" title="Direct link to Change more in Flightdeck, with no restart" translate="no">​</a></h2>
<p>Providers can be added, turned on and off, and removed in Flightdeck. Retry,
circuit breaker, timeouts and failover can be changed globally or per provider,
with history and rollback. Each workspace has its own response cache and turns it
on itself. Prompts can be kept for audit, apart from the signed record, and
deleted after a retention period you set. See
<a class="" href="https://dvarahq.com/docs/resilience#change-settings-in-flightdeck">Resilience</a> and
<a class="" href="https://dvarahq.com/docs/flight-deck/operations">Operations</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="more-from-each-provider">More from each provider<a href="https://dvarahq.com/blog/dvara-1-10-0#more-from-each-provider" class="hash-link" aria-label="Direct link to More from each provider" title="Direct link to More from each provider" translate="no">​</a></h2>
<p>Groq and Cohere now take tools and images, and Mistral takes images. Every Mistral
model family, including Pixtral and Codestral, reaches Mistral by its name, with no
route needed. See <a class="" href="https://dvarahq.com/docs/multimodal">Multimodal</a>.</p>
<p>The Enterprise Gateway also does more work per CPU core. On a laptop benchmark it
went from stalling near 3,100 small requests a second, with the CPU idle, to 5,000
a second with every request served.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-this-before-upgrading">Read this before upgrading<a href="https://dvarahq.com/blog/dvara-1-10-0#read-this-before-upgrading" class="hash-link" aria-label="Direct link to Read this before upgrading" title="Direct link to Read this before upgrading" translate="no">​</a></h2>
<p>Most of 1.10.0 needs no action. Check these first:</p>
<ol>
<li class="">The Gateways' cluster has a new port, <code>7800</code>, and new metric and health
names. Update network rules, dashboards and alerts that name the old ones.</li>
<li class="">Gateway pods talk TLS to each other. The Helm chart sets this up. With
Compose or VMs and more than one Gateway, give each one a certificate.</li>
<li class="">Data-residency and budget rules that cannot check now refuse.</li>
<li class="">Each workspace's response cache is off until the workspace turns it on.</li>
<li class="">Auto-kill is on by default, so a looping agent session ends. There is no
message-count cap by default.</li>
</ol>
<p>Flightdeck applies the database changes when it starts. The
<a class="" href="https://dvarahq.com/docs/releases-and-upgrading#moving-to-1100">upgrade guide</a> has every change and
how to keep the old behaviour.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-dvara-110">Try DVARA 1.10<a href="https://dvarahq.com/blog/dvara-1-10-0#try-dvara-110" class="hash-link" aria-label="Direct link to Try DVARA 1.10" title="Direct link to Try DVARA 1.10" translate="no">​</a></h2>
<p>The images <code>ghcr.io/dvarahq/dvara-gateway:1.10.0</code> and
<code>ghcr.io/dvarahq/dvara-flightdeck:1.10.0</code> and the Helm chart 1.10.0 are out now.
DVARA Open Source 1.10.0 is out too: get the
<a href="https://github.com/dvarahq/dvara/releases/tag/1.10.0" target="_blank" rel="noopener noreferrer" class="">release</a>, follow the
<a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a>, or
<a class="" href="https://dvarahq.com/book-a-demo">book a walkthrough</a> of the Enterprise platform.</p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
            <category>open-source</category>
        </item>
        <item>
            <title><![CDATA[DVARA 1.9.0 — Govern coding agents, MCP and A2A]]></title>
            <link>https://dvarahq.com/blog/dvara-1-9-0</link>
            <guid>https://dvarahq.com/blog/dvara-1-9-0</guid>
            <pubDate>Mon, 12 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DVARA 1.9 governs coding agents like Codex CLI, Gemini CLI and Copilot, serves the newest MCP and A2A versions, and signs and checks agent delegation chains.]]></description>
            <content:encoded><![CDATA[<p>Coding agents now make a large share of an engineering team's model calls. Most
of those calls go straight from a laptop to a provider, outside your keys,
policies, budgets and audit trail.</p>
<p>DVARA 1.9.0 brings them back onto the governed path. It also serves the newest
MCP and A2A protocol versions, and it signs agent-to-agent delegation so
each hop can prove whose authority it carries.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="govern-coding-agents">Govern coding agents<a href="https://dvarahq.com/blog/dvara-1-9-0#govern-coding-agents" class="hash-link" aria-label="Direct link to Govern coding agents" title="Direct link to Govern coding agents" translate="no">​</a></h2>
<p>Point a coding agent at DVARA, and every model call it makes goes through your
keys, policies, guardrails, budgets, metering and audit. 1.9.0 supports:</p>
<ul>
<li class=""><strong>Codex CLI</strong>, through the Responses API, on its default settings.</li>
<li class=""><strong>Gemini CLI</strong>, through a new Gemini API at <code>/v1/gemini</code>.</li>
<li class=""><strong>GitHub Copilot</strong>: the Copilot CLI and VS Code Copilot Chat's Custom
Endpoint models.</li>
<li class=""><strong>OpenCode</strong>, through Chat Completions.</li>
<li class=""><strong>Cursor</strong>, as a preview, through a base URL at <code>/v1/cursor</code>, in chat and
agent modes.</li>
<li class=""><strong>Claude Code</strong>, as before, through <code>/v1/messages</code>.</li>
</ul>
<p>Copilot's inline completions, cloud agent and code review stay on GitHub. No
gateway can govern them.</p>
<p>Tools that run at the provider, such as web search and code interpreter, are
refused. DVARA cannot check what they do, so it does not let them through. Each
setup page shows how to turn them off in the client. See
<a class="" href="https://dvarahq.com/docs/integrations/codex-cli">Codex CLI</a>,
<a class="" href="https://dvarahq.com/docs/integrations/gemini-cli">Gemini CLI</a>,
<a class="" href="https://dvarahq.com/docs/integrations/github-copilot">GitHub Copilot</a>,
<a class="" href="https://dvarahq.com/docs/integrations/opencode">OpenCode</a> and
<a class="" href="https://dvarahq.com/docs/integrations/cursor">Cursor</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="serve-the-newest-mcp-version">Serve the newest MCP version<a href="https://dvarahq.com/blog/dvara-1-9-0#serve-the-newest-mcp-version" class="hash-link" aria-label="Direct link to Serve the newest MCP version" title="Direct link to Serve the newest MCP version" translate="no">​</a></h2>
<p>DVARA now serves MCP 2026-07-28, the stateless version, beside 2025-11-25 and
2025-06-18. It passes the official MCP conformance suite.</p>
<p>Each governed server also has its own address, <code>/mcp/{serverId}</code>. A client that
connects there sees just that server's tools, with their own names.</p>
<p>DVARA can now sign in to an upstream MCP server in more ways: static headers,
OAuth client credentials, mutual TLS, AWS SigV4 and OAuth token exchange. See
<a class="" href="https://dvarahq.com/docs/mcp-gateway/upstream-auth">Authenticate to an upstream</a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="speak-a2a-10-and-sign-delegation-chains">Speak A2A 1.0, and sign delegation chains<a href="https://dvarahq.com/blog/dvara-1-9-0#speak-a2a-10-and-sign-delegation-chains" class="hash-link" aria-label="Direct link to Speak A2A 1.0, and sign delegation chains" title="Direct link to Speak A2A 1.0, and sign delegation chains" translate="no">​</a></h2>
<p>DVARA serves A2A 1.0 by default, beside 0.3. Both official A2A SDKs and the
official test kit pass against it.</p>
<p>When one agent hands work to another, DVARA now signs the delegation chain. It
checks the chain at every hop, ties it to one workspace, and lets you revoke a
single chain or everything one issuer signed. A hop that carries a signed chain
cannot claim more authority than it was given. See the
<a class="" href="https://dvarahq.com/docs/a2a-delegation-guard">A2A delegation guard</a>.</p>
<p>The Agent Card DVARA serves is written in your client's A2A version and signed.
Flightdeck also adds an agent catalog, so you can register a known agent in one
click.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="make-passing-on-a-callers-token-a-choice">Make passing on a caller's token a choice<a href="https://dvarahq.com/blog/dvara-1-9-0#make-passing-on-a-callers-token-a-choice" class="hash-link" aria-label="Direct link to Make passing on a caller's token a choice" title="Direct link to Make passing on a caller's token a choice" translate="no">​</a></h2>
<p>Some upstream servers and agents want the caller's own token. Sending it on
unchanged lets anyone holding that token reach further than you meant. In 1.9.0
it is a choice you make on each server or agent, and each call that does it is
recorded. Where the upstream supports it, DVARA can trade the token for a new
one made only for that upstream.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="see-more-of-where-spend-goes">See more of where spend goes<a href="https://dvarahq.com/blog/dvara-1-9-0#see-more-of-where-spend-goes" class="hash-link" aria-label="Direct link to See more of where spend goes" title="Direct link to See more of where spend goes" translate="no">​</a></h2>
<p>Usage now shows spend by end user and by tag. It counts cached, cache-write and
reasoning tokens, and the access log records them. A stream that fails is
audited as failed, not as a served answer. Flightdeck warns you when a
workspace's IP rules are saved but not enforced.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-this-before-upgrading">Read this before upgrading<a href="https://dvarahq.com/blog/dvara-1-9-0#read-this-before-upgrading" class="hash-link" aria-label="Direct link to Read this before upgrading" title="Direct link to Read this before upgrading" translate="no">​</a></h2>
<p>Check four things before you move production traffic:</p>
<ol>
<li class="">A refused A2A JSON-RPC call now answers HTTP <code>200</code>, with the error in the
JSON-RPC body, so the official SDKs can read it. A bad key still gets <code>401</code>.
Set <code>a2a.protocol.json-rpc-error-status=refusal</code> to keep the old status
codes.</li>
<li class="">Passing a caller's own token to an upstream is now an explicit, audited
choice. The upgrade switches existing delegated servers and agents to it, so
they keep working.</li>
<li class="">Tools that run at the provider are refused on <code>/v1/responses</code> and the Gemini
API.</li>
<li class="">Workspace IP rules apply only when IP access is on at the Gateway, with
<code>DVARA_LLM_GATEWAY_IP_ACCESS_ENABLED=true</code>.</li>
</ol>
<p>Flightdeck applies the database changes when it starts. The
<a class="" href="https://dvarahq.com/docs/releases-and-upgrading#moving-to-190">upgrade guide</a> has the details.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-dvara-19">Try DVARA 1.9<a href="https://dvarahq.com/blog/dvara-1-9-0#try-dvara-19" class="hash-link" aria-label="Direct link to Try DVARA 1.9" title="Direct link to Try DVARA 1.9" translate="no">​</a></h2>
<p>The images <code>ghcr.io/dvarahq/dvara-gateway:1.9.0</code> and
<code>ghcr.io/dvarahq/dvara-flightdeck:1.9.0</code> and the Helm chart 1.9.0 are out now.
DVARA Open Source 1.9.0 is out too: get the
<a href="https://github.com/dvarahq/dvara/releases/tag/1.9.0" target="_blank" rel="noopener noreferrer" class="">release</a>, follow the
<a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a>, or
<a class="" href="https://dvarahq.com/book-a-demo">book a walkthrough</a> of the Enterprise platform.</p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
            <category>open-source</category>
        </item>
        <item>
            <title><![CDATA[Agent A Asks Agent B, Agent B Asks Agent A: How to Prevent Recursive Delegation]]></title>
            <link>https://dvarahq.com/blog/prevent-recursive-agent-delegation</link>
            <guid>https://dvarahq.com/blog/prevent-recursive-agent-delegation</guid>
            <pubDate>Wed, 30 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Govern recursive agent delegation with signed chains in DVARA. Learn how strict cycle checks, required chains, and session hop budgets work in version 1.8.5.]]></description>
            <content:encoded><![CDATA[<p><em>Agent A asks Agent B to do a task. Agent B needs something from Agent A, so it asks. Agent A, to answer, asks Agent B again. How do you stop that recursion?</em></p>
<p>Carry a signed <strong>delegation chain</strong> with the call: a record of which agents it has already passed through. Check it before calling the next agent. With strict agent matching and enforcement turned on, a return to an agent already in the chain stops there. In DVARA, you can also allow a return for a different skill. The choice matters, as does requiring agents to pass the chain along: a signature prevents edits, but it cannot stop an agent from leaving the chain out.</p>
<!-- -->
<p><em>Updated October 2, 2026 for DVARA 1.8.5.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-two-good-agents-make-one-loop">The problem: two good agents make one loop<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#the-problem-two-good-agents-make-one-loop" class="hash-link" aria-label="Direct link to The problem: two good agents make one loop" title="Direct link to The problem: two good agents make one loop" translate="no">​</a></h2>
<p>Take two agents in a travel workspace. <strong>Planner</strong> builds trip plans and <strong>Budget</strong> prices them.</p>
<p>A user asks Planner for "a 3-day Lisbon trip under €1,500". Planner asks Budget to price its draft. Budget needs the day-by-day itinerary, so it asks Planner. To build the itinerary, Planner needs a price, so it asks Budget again.</p>
<p>Each agent is asking for information it needs. The problem is that neither knows the other is waiting on it. Every trip around that loop adds work and model calls to your bill.</p>
<p>Adding agents just makes the loop harder to see. Planner → Budget → FX → Tax → Planner has the same problem, with more steps before anyone returns to Planner.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-usual-fixes-fall-short">Why the usual fixes fall short<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#why-the-usual-fixes-fall-short" class="hash-link" aria-label="Direct link to Why the usual fixes fall short" title="Direct link to Why the usual fixes fall short" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="timeouts">Timeouts<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#timeouts" class="hash-link" aria-label="Direct link to Timeouts" title="Direct link to Timeouts" translate="no">​</a></h3>
<p>A timeout limits how long a loop runs, but the work before it expires has already happened. At one hop every two seconds, a 60-second timeout leaves room for roughly 30 hops.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="loop-detectors-and-rate-limits">Loop detectors and rate limits<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#loop-detectors-and-rate-limits" class="hash-link" aria-label="Direct link to Loop detectors and rate limits" title="Direct link to Loop detectors and rate limits" translate="no">​</a></h3>
<p>A rate limit stops calls after they reach a threshold. A session loop detector looks for repeated patterns. Both let some work through before acting. For example, a detector that waits for three repetitions of a short cycle handles Planner and Budget like this:</p>
<table><thead><tr><th>Hop</th><th>Call</th><th>Skill</th><th>Result</th></tr></thead><tbody><tr><td>1</td><td>user → Planner</td><td><code>plan</code></td><td>allowed</td></tr><tr><td>2</td><td>Planner → Budget</td><td><code>price</code></td><td>allowed</td></tr><tr><td>3</td><td>Budget → Planner</td><td><code>itinerary</code></td><td>allowed</td></tr><tr><td>4</td><td>Planner → Budget</td><td><code>price</code></td><td>allowed</td></tr><tr><td>5</td><td>Budget → Planner</td><td><code>itinerary</code></td><td>allowed</td></tr><tr><td>6</td><td>Planner → Budget</td><td><code>price</code></td><td>allowed</td></tr><tr><td>7</td><td>Budget → Planner</td><td><code>itinerary</code></td><td>refused</td></tr></tbody></table>
<p>Six hops go through before the seventh is refused. The detector also needs to know which calls belong together. DVARA looks for <code>X-Session-Id</code> first, then the session in the delegation chain, then the message's <code>contextId</code>. Losing the header alone is therefore recoverable. Lose all three, and DVARA has to create a new session id; it can no longer connect the call to the earlier work.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-per-session-list-of-agents">A per-session list of agents<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#a-per-session-list-of-agents" class="hash-link" aria-label="Direct link to A per-session list of agents" title="Direct link to A per-session list of agents" translate="no">​</a></h3>
<p>You could record every agent a session has touched and refuse a repeat. But that refuses valid work. Planner may call Budget and Weather at the same time, and later call Budget again for a different trip in the same session. One shared list mixes those branches and refuses calls that are fine.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="letting-agents-name-themselves">Letting agents name themselves<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#letting-agents-name-themselves" class="hash-link" aria-label="Direct link to Letting agents name themselves" title="Direct link to Letting agents name themselves" translate="no">​</a></h3>
<p>If the check relies on a header in which the caller names itself, any caller can claim any name, or leave out the part of the chain that would get it refused.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-general-answer-rule-by-rule">The general answer, rule by rule<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#the-general-answer-rule-by-rule" class="hash-link" aria-label="Direct link to The general answer, rule by rule" title="Direct link to The general answer, rule by rule" translate="no">​</a></h2>
<p>A gateway, message bus or orchestrator can make the decision before forwarding each call. It needs a few things from the caller and a few rules of its own:</p>
<ol>
<li class=""><strong>Put the chain on the call.</strong> Each call carries the path that led to it: <code>planner → budget</code>. The agent that receives it passes it on with its own onward calls, the way it already forwards a trace id.</li>
<li class=""><strong>Refuse a cycle.</strong> If the target is already in the chain, refuse the call. You can choose how strict to be. "Same agent" refuses any return to an agent. "Same agent and same job" lets Planner be asked for a draft and later for a check, but refuses the same request twice.</li>
<li class=""><strong>Set a depth limit.</strong> Decide how many agents a task should need. Refuse the next hop when it exceeds that depth, even if every agent or skill name is different.</li>
<li class=""><strong>Let the router own and sign the chain.</strong> The component that forwards the call writes the chain and signs it. An agent can carry the chain but cannot change it. A chain that was changed, has expired, or belongs to another workspace or session is refused. Fail closed: a bad chain is never treated as "no chain".</li>
<li class=""><strong>Keep parallel branches separate.</strong> Check <code>planner → budget</code> against its own chain and <code>planner → weather</code> against its own. A session detector running behind that check must respect those branches too.</li>
<li class=""><strong>Expire the chain.</strong> Count its lifetime from the first call, without renewing it at each hop. Require the chain on onward calls so an agent cannot reset its depth or age by omitting it. Use a separate session hop budget to limit total work across branches.</li>
<li class=""><strong>Tell the caller to stop this delegation.</strong> Return <code>409 Conflict</code> with a cycle error code. The agent needs a handler for it: finish with the information available, return an incomplete result, or ask a person for help. Retrying the same delegation will fail again.</li>
</ol>
<p>Keeping the chain on the call means another gateway with the same signing key can check it without fetching its history. That check is stateless. Counting calls across a session is a separate job, and multiple gateways need shared accounting to enforce one session limit.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-dvara-does-it">How DVARA does it<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#how-dvara-does-it" class="hash-link" aria-label="Direct link to How DVARA does it" title="Direct link to How DVARA does it" translate="no">​</a></h2>
<p>The DVARA A2A Gateway has checked delegation chains since 1.8.3. Version 1.8.5 adds a hardened preset and a session hop budget, and fixes how the session detector handles parallel sibling calls.</p>
<p>These are Enterprise platform capabilities. You can use them without a licence in Development for non-production work within 3 workspaces and 100,000 governed calls a month. Production rights and support require a licence. The DVARA Open Source distribution does not include the A2A Gateway.</p>
<p>With a usable signing key and the guard enabled, the gateway signs a delegation chain and sends it with each hop, in a header called <code>X-DVARA-Delegation</code>. The agent passes it back to the gateway on its next outgoing call, together with its session id. Before the gateway forwards a hop, the guard checks the chain for a cycle and for depth. A refused hop never reaches the peer, is never billed, and does not count toward the workspace's monthly hop cap.</p>
<p>Here is the same Planner and Budget example in <code>enforce</code> mode, with each agent preserving its chain:</p>
<table><thead><tr><th>Hop</th><th>Call</th><th>Chain on arrival</th><th>Default rule (same agent and skill)</th><th>Strict rule (same agent)</th></tr></thead><tbody><tr><td>1</td><td>user → Planner <code>plan</code></td><td>none</td><td>allowed</td><td>allowed</td></tr><tr><td>2</td><td>Planner → Budget <code>price</code></td><td><code>planner</code></td><td>allowed</td><td>allowed</td></tr><tr><td>3</td><td>Budget → Planner <code>itinerary</code></td><td><code>planner → budget</code></td><td>allowed</td><td><strong>refused</strong></td></tr><tr><td>4</td><td>Planner → Budget <code>price</code></td><td><code>planner → budget → planner</code></td><td><strong>refused</strong></td><td>never sent</td></tr></tbody></table>
<p>With the strict rule, Budget gets this on hop 3, and Planner is never called:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">409 a2a_delegation_cycle</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Delegation cycle: agent planner is already in chain planner → budget.</span><br></div></code></pre></div></div>
<p>Budget now has a choice to make in its error handler. It can price the draft it already has, or return an incomplete result if the draft is not enough. It must not retry <code>a2a_delegation_cycle</code>. DVARA stops the call at hop 3; Budget still has to decide how to answer Planner.</p>
<p>The default rule, same agent and same skill, lets Budget ask Planner for the itinerary once, because that is a new job. It refuses the repeat on hop 4. The default depth limit is 8, and each workspace picks its own rule and depth.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="enable-the-hardened-preset-in-185">Enable the hardened preset in 1.8.5<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#enable-the-hardened-preset-in-185" class="hash-link" aria-label="Direct link to Enable the hardened preset in 1.8.5" title="Direct link to Enable the hardened preset in 1.8.5" translate="no">​</a></h2>
<p>If you want to stop the first return to an agent, choose <code>a2a.delegation.preset: hardened</code> for the fleet or workspace. This is <strong>opt-in</strong>. It selects the <code>agent</code> rule, requires chains from agent-bound keys with the root-key exception below, and sets a 200-hop session budget unless you specify another value. You still choose the mode separately: <code>observe</code> records what would be refused; <code>enforce</code> refuses it.</p>
<p>Existing installations keep the standard defaults: <code>mode: enforce</code>, <code>cycle-rule: agent-skill</code>, <code>max-depth: 8</code>, and <code>require-chain-for-agents: false</code>, all under <code>a2a.delegation</code>. The session safety budget is off under that preset. <code>a2a.delegation.chain-ttl</code> defaults to 15 minutes from the root call. The <a class="" href="https://dvarahq.com/docs/a2a-delegation-guard">delegation guard guide</a> covers the full configuration.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="check-that-the-gateway-can-sign-chains">Check that the gateway can sign chains<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#check-that-the-gateway-can-sign-chains" class="hash-link" aria-label="Direct link to Check that the gateway can sign chains" title="Direct link to Check that the gateway can sign chains" translate="no">​</a></h3>
<p>Set <code>DVARA_A2A_DELEGATION_SIGNING_SECRET</code> to a private random secret of at least 32 bytes, shared by the gateway pods. If it is unset, DVARA derives a delegation key from a usable <code>DVARA_AUDIT_HMAC_SECRET</code>. Published placeholder secrets are not usable signing keys.</p>
<p>Check gateway status before testing a loop. In 1.8.5, a production profile refuses to start without a signing secret if the fleet asks for an active guard. A development profile can start with the guard effectively <code>off</code>. Gateway status shows the configured and effective fleet modes; check those alongside your workspace's mode. An <code>enforce</code> setting is no help if the pod cannot sign chains.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="separate-root-keys-from-onward-agent-keys">Separate root keys from onward-agent keys<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#separate-root-keys-from-onward-agent-keys" class="hash-link" aria-label="Direct link to Separate root keys from onward-agent keys" title="Direct link to Separate root keys from onward-agent keys" translate="no">​</a></h3>
<p>Bind each agent's API key to that agent. When chains are required and the guard enforces them, an ordinary agent-bound key without a chain receives <code>403 a2a_delegation_chain_required</code>.</p>
<p>Some calls have to start somewhere. A key marked <strong>“may start chains”</strong> can start new work without an incoming chain; DVARA puts its bound agent at the beginning of the new chain. Unbound application keys can start roots too. Keep those credentials at trusted entry points and give agents separate keys for onward calls. Otherwise, an agent can use its root permission to start again.</p>
<p>Under the standard defaults, dropping the token from both the header and message metadata starts a fresh chain. DVARA also accepts the token in <code>message.metadata["dvara.delegation"]</code>, so dropping only the header does not reset the chain when that metadata remains. The session detector still provides a backstop when calls remain correlated.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="observe-the-effect-before-enforcing">Observe the effect before enforcing<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#observe-the-effect-before-enforcing" class="hash-link" aria-label="Direct link to Observe the effect before enforcing" title="Direct link to Observe the effect before enforcing" translate="no">​</a></h3>
<p>Start in <code>observe</code> mode. Review would-refuse events, the Console's hop markers, and the depth metric before switching to <code>enforce</code>. In observe mode, cycle, depth, missing-chain and hop-budget violations are recorded without blocking the call. Invalid signatures still fail closed, and other controls such as session loop detection remain active.</p>
<p>The API-key binding identifies the caller. Possessing a chain does not establish an agent's identity. DVARA removes <code>dvara.delegation</code> metadata from replies, including nested values and escaped JSON keys in streams, and keeps the token out of its delegation logs. It does not scrub arbitrary text in which a peer has copied the token. Agents need to treat it as sensitive too.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bound-session-work-separately-from-billing">Bound session work separately from billing<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#bound-session-work-separately-from-billing" class="hash-link" aria-label="Direct link to Bound session work separately from billing" title="Direct link to Bound session work separately from billing" translate="no">​</a></h2>
<p>In 1.8.5, <code>a2a.delegation.max-hops-per-session</code> limits how many hops a session forwards. It is separate from chain depth and from the workspace's monthly billing allowance. Under the standard preset, <code>0</code> means no safety limit. Under hardened, <code>0</code> selects the preset's 200-hop limit; an explicit positive value replaces it.</p>
<p>Once the budget is used up, <code>enforce</code> mode returns <code>409 a2a_delegation_hop_budget_exceeded</code> before the next call reaches the peer. Handle that as a stop for this work, not a reason to open another session. If DVARA cannot read the count, hardened enforcement returns <code>503 a2a_delegation_hop_budget_unavailable</code>. Standard mode lets that call through uncounted and records a warning and metric. In <code>observe</code>, exceeding the budget is recorded but does not block the call.</p>
<p>If you run several gateway pods, make sure they share the same accounting cluster. The local fallback only counts calls on its own pod. Separate clusters, including those split by a partition, each count their own traffic. Cluster-backed counts also expire after an hour idle. These are limits on session work, not a permanent spending cap.</p>
<p>Do not rely on the monthly A2A allowance to stop a runaway session. An unlimited allowance or billable overage permits more calls, and an unavailable usage count does not stop them either. Set the session safety budget for that purpose.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-stays-as-a-backstop">What stays as a backstop<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#what-stays-as-a-backstop" class="hash-link" aria-label="Direct link to What stays as a backstop" title="Direct link to What stays as a backstop" translate="no">​</a></h2>
<p>The delegation guard works alongside session loop detection, the session kill switch, billing controls, A2A policies and configured human-approval rules. In 1.8.5, identical sibling hops carrying the same verified parent chain count once for repetition and cycle detection. Each still counts toward the session rate limit and any enabled safety budget. A rate refusal uses <code>429 a2a_session_rate_limited</code> and does not kill the session.</p>
<p>That sibling handling depends on a verified chain. Calls without one still go through the session detector's heuristic checks. Across multiple pods, shared loop history depends on the accounting cluster; a local fallback sees only the calls on that pod.</p>
<p>One choice deserves particular care: the default <code>agent-skill</code> rule trusts the skill name the caller sends. Changing that name avoids a cycle match, although a preserved chain will still hit its depth limit. If a return to the same agent should always stop, use strict matching. Keep onward calls on keys that require a chain, and preserve their session context, so those checks have the history they need.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-it">Try it<a href="https://dvarahq.com/blog/prevent-recursive-agent-delegation#try-it" class="hash-link" aria-label="Direct link to Try it" title="Direct link to Try it" translate="no">​</a></h2>
<p><a class="" href="https://dvarahq.com/docs/a2a-delegation-guard">Stop recursive delegation between AI agents</a> covers the settings, error codes, and what an agent builder must forward. <a class="" href="https://dvarahq.com/docs/native-a2a">Govern peer-agent calls with the A2A Gateway</a> covers registration, policy, PII and approvals on each hop.</p>]]></content:encoded>
            <category>agentic-ai</category>
            <category>governance</category>
            <category>ai-safety</category>
        </item>
        <item>
            <title><![CDATA[DVARA 1.8.0 — Govern models, tools, and agents]]></title>
            <link>https://dvarahq.com/blog/dvara-1-8-0</link>
            <guid>https://dvarahq.com/blog/dvara-1-8-0</guid>
            <pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DVARA 1.8 adds an Open Source foundation, enforced API scopes, safer PII and streaming controls, and stronger evidence across every AI call.]]></description>
            <content:encoded><![CDATA[<p>AI applications rarely stop at one model call. They call different providers,
use tools through MCP, and increasingly hand work to other agents. Each hop is a
place where policy, data, spend, or evidence can fall apart.</p>
<p>DVARA 1.8.0 puts those calls on one governed path. It introduces a clean Open
Source foundation for LLM traffic and strengthens the Enterprise controls used
across models, tools, and agents.</p>
<img src="https://dvarahq.com/img/blog/dvara-1-8-0-governed-path-light.svg" alt="DVARA 1.8 governs model, tool, and agent calls through one runtime control point" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="https://dvarahq.com/img/blog/dvara-1-8-0-governed-path-dark.svg" alt="DVARA 1.8 governs model, tool, and agent calls through one runtime control point" class="themedComponent_mlkZ themedComponent--dark_xIcU">
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="start-with-open-source">Start with Open Source<a href="https://dvarahq.com/blog/dvara-1-8-0#start-with-open-source" class="hash-link" aria-label="Direct link to Start with Open Source" title="Direct link to Start with Open Source" translate="no">​</a></h2>
<p>DVARA Open Source is an Apache-2.0 LLM Gateway that runs with file
configuration and no database. It gives you a practical way to put policy, PII
checks, guardrails, rate limits, and a local tamper-evident audit file in front
of model traffic.</p>
<p>You can run it as a standalone gateway or embed its governance libraries in a
Spring Boot application. The configuration lives in <code>gateway.yaml</code>, so the
first deployment is easy to inspect, version, and reproduce.</p>
<p>The Enterprise platform builds on the same foundation. It adds Flightdeck,
central persistence, fleet operations, advanced LLM controls, and the DVARA MCP
and A2A Gateways.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="make-permissions-real">Make permissions real<a href="https://dvarahq.com/blog/dvara-1-8-0#make-permissions-real" class="hash-link" aria-label="Direct link to Make permissions real" title="Direct link to Make permissions real" translate="no">​</a></h2>
<p>API-key scopes are now enforced across the LLM, MCP, and A2A planes. A key can
be limited to the operations its workload needs—for example, model discovery,
embeddings, MCP reads, tool calls, or A2A sends.</p>
<p>A valid key used outside its scopes is refused with <code>403</code>. That turns scopes
from descriptive metadata into an authorization boundary.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="say-exactly-what-happens-to-sensitive-data">Say exactly what happens to sensitive data<a href="https://dvarahq.com/blog/dvara-1-8-0#say-exactly-what-happens-to-sensitive-data" class="hash-link" aria-label="Direct link to Say exactly what happens to sensitive data" title="Direct link to Say exactly what happens to sensitive data" translate="no">​</a></h2>
<p>In 1.8, <code>REDACT</code> and <code>TOKENIZE</code> mean different things:</p>
<table><thead><tr><th>Action</th><th>What happens</th></tr></thead><tbody><tr><td><code>LOG</code></td><td>Record the finding and leave the value unchanged.</td></tr><tr><td><code>BLOCK</code></td><td>Refuse the request.</td></tr><tr><td><code>REDACT</code></td><td>Replace the value irreversibly. The original is not stored.</td></tr><tr><td><code>TOKENIZE</code></td><td>Replace request-side data with a reversible token when the Enterprise token store is available.</td></tr></tbody></table>
<p>The distinction matters. A compliance review should not have to guess whether
“redacted” data can later be recovered.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hold-protected-streams-until-the-decision-is-final">Hold protected streams until the decision is final<a href="https://dvarahq.com/blog/dvara-1-8-0#hold-protected-streams-until-the-decision-is-final" class="hash-link" aria-label="Direct link to Hold protected streams until the decision is final" title="Direct link to Hold protected streams until the decision is final" translate="no">​</a></h2>
<p>An observational streaming policy can still deliver events immediately while
DVARA records evidence. A control that can block or transform content now holds
the complete response, makes one decision, and then releases governed content
or refuses it.</p>
<p>DVARA also treats a provider stream that ends without a finish reason as an
error. A truncated answer no longer looks like a successful, billable response.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="govern-tools-and-agent-handoffs">Govern tools and agent handoffs<a href="https://dvarahq.com/blog/dvara-1-8-0#govern-tools-and-agent-handoffs" class="hash-link" aria-label="Direct link to Govern tools and agent handoffs" title="Direct link to Govern tools and agent handoffs" translate="no">​</a></h2>
<p>The Enterprise MCP and A2A Gateways apply the same identity, policy, PII,
budget, approval, loop-detection, and audit controls to tool calls and agent
handoffs.</p>
<p>Streaming LLM tool calls now retain their IDs, types, function names, and
arguments through provider translation. DVARA records the intent; your
application still decides whether to execute the tool.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="give-operators-evidence-they-can-use">Give operators evidence they can use<a href="https://dvarahq.com/blog/dvara-1-8-0#give-operators-evidence-they-can-use" class="hash-link" aria-label="Direct link to Give operators evidence they can use" title="Direct link to Give operators evidence they can use" translate="no">​</a></h2>
<p>The release adds clearer operational signals around refused requests, protected
stream buffers, incomplete streams, webhook delivery, cluster membership, and
provider failures. File-backed audit chains can be verified with coverage as
well as a pass/fail result.</p>
<p>Provider failures are also classified more carefully. A bad key or most other
provider <code>4xx</code> responses no longer open a shared circuit for every workspace.
Rate limits, timeouts, and fallback behavior now follow the reason for the
failure instead of treating every error alike.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-this-before-upgrading">Read this before upgrading<a href="https://dvarahq.com/blog/dvara-1-8-0#read-this-before-upgrading" class="hash-link" aria-label="Direct link to Read this before upgrading" title="Direct link to Read this before upgrading" translate="no">​</a></h2>
<p>Several controls that were previously permissive or inactive now take effect.
Check these before moving production traffic:</p>
<ol>
<li class="">A non-empty API-key scope list is enforced.</li>
<li class=""><code>REDACT</code> is irreversible; use <code>TOKENIZE</code> when you need reversible request-side substitution.</li>
<li class="">Tool allowlists and denylists now match and enforce their rules.</li>
<li class="">Context limits come from the serving provider, and the maximum-input-token guardrail is off by default.</li>
<li class="">Provider <code>4xx</code> responses other than <code>408</code> and <code>429</code> do not count against the shared circuit breaker.</li>
</ol>
<p>The <a class="" href="https://dvarahq.com/docs/releases-and-upgrading">1.8 upgrade guide</a> covers the complete
preflight, rollout, verification, and rollback sequence.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-dvara-18">Try DVARA 1.8<a href="https://dvarahq.com/blog/dvara-1-8-0#try-dvara-18" class="hash-link" aria-label="Direct link to Try DVARA 1.8" title="Direct link to Try DVARA 1.8" translate="no">​</a></h2>
<p>Download the <a href="https://github.com/dvarahq/dvara/releases/tag/1.8.0" target="_blank" rel="noopener noreferrer" class="">Open Source 1.8.0 release</a>,
follow the <a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a>, compare
<a class="" href="https://dvarahq.com/docs/deployment/editions-and-images">capability availability</a>, or
<a class="" href="https://dvarahq.com/book-a-demo">book a walkthrough</a> for the Enterprise platform.</p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
            <category>open-source</category>
        </item>
        <item>
            <title><![CDATA[Shadow AI: you can't govern what you can't see]]></title>
            <link>https://dvarahq.com/blog/shadow-ai-visibility-problem</link>
            <guid>https://dvarahq.com/blog/shadow-ai-visibility-problem</guid>
            <pubDate>Fri, 21 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Shadow AI is a visibility problem before it is a policy problem. How it forms, the four questions that measure it, and what one governed path recorded.]]></description>
            <content:encoded><![CDATA[<p>How many LLM calls did your company make yesterday?</p>
<p>Most platform leads can't answer. Not roughly, not which providers, not which teams. That gap has a name — shadow AI — and it is a visibility problem long before it is a policy problem.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-it-happens">How it happens<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#how-it-happens" class="hash-link" aria-label="Direct link to How it happens" title="Direct link to How it happens" translate="no">​</a></h2>
<p>Nobody decides to have shadow AI. It arrives one reasonable decision at a time.</p>
<p>One team adds an OpenAI key to a service. Another wires up Anthropic for a different feature. A third is calling a model through a SaaS tool that nobody logged as an AI vendor. A fourth is running an agent that calls tools on its own.</p>
<p>Four sensible decisions. Four paths out of your network, four sets of credentials, four logging conventions. No single place the traffic passes through — so no single place to see it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="four-questions-that-measure-it">Four questions that measure it<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#four-questions-that-measure-it" class="hash-link" aria-label="Direct link to Four questions that measure it" title="Direct link to Four questions that measure it" translate="no">​</a></h2>
<p>Ask these about yesterday, not last quarter.</p>
<ol>
<li class=""><strong>Which applications called a model, and which model did each one use?</strong></li>
<li class=""><strong>What did they send?</strong> Would you know if customer data had left?</li>
<li class=""><strong>Who authorised it?</strong> For an agent: which tool did it call, on whose behalf?</li>
<li class=""><strong>Prove it.</strong> Produce a record you would hand an auditor.</li>
</ol>
<p>If those take a week of Slack archaeology, the problem isn't that your policy is badly written. There is nothing in the request path that could enforce a policy even if it were perfect.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-usual-answers-dont-close-it">Why the usual answers don't close it<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#why-the-usual-answers-dont-close-it" class="hash-link" aria-label="Direct link to Why the usual answers don't close it" title="Direct link to Why the usual answers don't close it" translate="no">​</a></h2>
<ul>
<li class=""><strong>A written policy</strong> describes intent. It doesn't change what happens on the next request.</li>
<li class=""><strong>Your API gateway</strong> sees an HTTPS POST to a hostname. Not the model, not the tokens, not the prompt, not the tool call.</li>
<li class=""><strong>Per-app logging</strong> is four formats in four places that stop the moment someone is in a hurry.</li>
<li class=""><strong>Provider dashboards</strong> are organised around billing, one per provider, and know nothing about that SaaS tool.</li>
</ul>
<p>Each is useful. None produces an inventory.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-fix-is-an-old-one">The fix is an old one<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#the-fix-is-an-old-one" class="hash-link" aria-label="Direct link to The fix is an old one" title="Direct link to The fix is an old one" translate="no">​</a></h2>
<p>When services multiplied, we stopped asking every team to get retries and mTLS right and put a service mesh in the path. When APIs multiplied, we put a gateway in front.</p>
<p>Same move. Route model traffic through one point and visibility stops depending on whether every team remembered. It becomes a property of the architecture.</p>
<p>The control has to sit <strong>in the request path</strong>. Something that observes and reports is a monitoring tool — if it can't refuse a call, it can't enforce anything.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-it-costs-to-adopt">What it costs to adopt<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#what-it-costs-to-adopt" class="hash-link" aria-label="Direct link to What it costs to adopt" title="Direct link to What it costs to adopt" translate="no">​</a></h2>
<p>Almost every provider now speaks an OpenAI-compatible API, so this is a base URL, not a rewrite.</p>
<p><strong>Python — before.</strong> Each service holds its own key and makes its own way out.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">client </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> OpenAI</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">api_key</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"OPENAI_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p><strong>After.</strong> Same SDK, same call sites.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">client </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> OpenAI</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    base_url</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"https://dvara.internal/v1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    api_key</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">os</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">environ</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"DVARA_API_KEY"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p><strong>Java, Spring AI.</strong> Configuration only — no Java changes.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spring</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ai</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">openai</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">base-url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">DVARA_URL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//localhost</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">8080/v1</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">api-key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">DVARA_API_KEY</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>One gotcha worth an afternoon of your life: <strong>Spring AI 2.0 uses the official OpenAI SDK, which POSTs to <code>${base-url}/chat/completions</code>. The base URL must include <code>/v1</code> yourself</strong> — unlike Spring AI 1.x, the SDK does not prepend it.</p>
<p>Agents travel the same road. Point the MCP client at the governed endpoint and tool calls are governed too:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spring</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ai</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">mcp</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">streamable-http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">connections</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">dvara</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">DVARA_MCP_URL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">http</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//localhost</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">8080/mcp</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>That matters more than it looks. Most AI-governance conversations stop at the model call. But an agent that can call tools can read a database, hit an internal API, or move money. <strong>The model call is the least dangerous thing an agent does.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-this-actually-produced">What this actually produced<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#what-this-actually-produced" class="hash-link" aria-label="Direct link to What this actually produced" title="Direct link to What this actually produced" translate="no">​</a></h2>
<p>Everything below is from a local run against the mock provider — no real provider traffic, so the model names are stand-ins. What is real is the record the gateway kept without any application being changed.</p>
<p>A hundred and twenty-four governed calls later, the inventory that did not exist:</p>
<table><thead><tr><th>Model</th><th>Calls</th><th>Avg latency</th></tr></thead><tbody><tr><td><code>mock/gpt-4o-mini</code></td><td>27</td><td>44.9 ms</td></tr><tr><td><code>mock/gpt-4o</code></td><td>21</td><td>59.2 ms</td></tr><tr><td><code>mock/claude-3-haiku</code></td><td>18</td><td>60.3 ms</td></tr><tr><td><code>mock/claude-3-5-sonnet</code></td><td>16</td><td>59.7 ms</td></tr><tr><td><code>mock/gemini-1.5-pro</code></td><td>15</td><td>55.9 ms</td></tr><tr><td><code>mock/gpt-4o-expensive</code></td><td>14</td><td>60.4 ms</td></tr><tr><td><code>mock/mistral-large</code></td><td>13</td><td>44.5 ms</td></tr></tbody></table>
<p><img decoding="async" loading="lazy" alt="The Console audit log filtered to gateway responses, each row showing its model, workspace, status and latency" src="https://dvarahq.com/assets/images/01-console-inventory-863896179af86a2eea73138a04fec268.png" width="2000" height="1333" class="img_ev3q"></p>
<p>That table is a <code>GROUP BY</code> over the stream above it — every governed call, with the model
it went to and the workspace it came from, in one place. Question one, answered by a query
rather than a week of archaeology.</p>
<p>Two requests carrying a US Social Security number were detected on the way out and recorded as <code>PII_DETECTED</code>, with the entity type — before the call left the boundary.</p>
<p><img decoding="async" loading="lazy" alt="An audit record showing PII detected on an outbound request" src="https://dvarahq.com/assets/images/02-audit-pii-577c21ad2fa7eb8daa04cc2d1d01d90c.png" width="2000" height="1333" class="img_ev3q"></p>
<p>And the audit trail is a hash chain rather than a log file that claims to be one. 429 signed
envelopes, each carrying its own HMAC and its predecessor's hash.</p>
<p>The chain is <strong>per writing process</strong>, which is the part worth knowing before you check one: this
install had four — the gateway, the console, and two more from restarts — so there are four
chains, not one, and each numbers its own envelopes from 1. Checking each chain against itself:</p>
<ul>
<li class="">every chain runs <strong>1 to N with no gaps</strong> (420, 4, 3 and 2 envelopes)</li>
<li class=""><strong>zero</strong> envelopes, across all 429, whose <code>previous_hash</code> failed to match the preceding <code>hmac</code></li>
</ul>
<p>Compare envelopes across chains instead and you will get breaks that are not there — the sequence
numbers interleave. It is the first thing to get wrong.</p>
<p>Remove or edit a record and the chain stops matching at that point. That is the difference between logs and evidence.</p>
<p>Finally, visibility is only worth having if you can act on it. A rule denying one model:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">rules</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> no</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">expensive</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">conditions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">model</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">denylist</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">mock/gpt</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">4o</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">expensive</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">action</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DENY</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="A request refused with 403 policy_denied, naming the rule that fired" src="https://dvarahq.com/assets/images/03-policy-denied-e5ed93405897c78291dab537d18f7ae5.png" width="2000" height="1333" class="img_ev3q"></p>
<p>The refusal names the rule that fired, and it happens <strong>before the provider is called</strong> — nothing sent, nothing billed. Every other model keeps working.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-honest-objection">The honest objection<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#the-honest-objection" class="hash-link" aria-label="Direct link to The honest objection" title="Direct link to The honest objection" translate="no">​</a></h2>
<p>One point in the path is one point of failure, and anyone who has run infrastructure will say so immediately. That has to be answered in the design, not the pitch: capability-aware failover so one provider outage doesn't take the path down, circuit breakers, and the ability to run inside your own perimeter so the control point isn't a new third party holding your prompts.</p>
<p>If the thing you added for governance becomes the reason you're down, you've made the system worse, and no audit trail compensates for that.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/shadow-ai-visibility-problem#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p>This is what we build. DVARA is a self-hosted control plane that sits in the request path for model calls, MCP tool calls and agent-to-agent hops, applying one policy set and one audit trail across all three. Everything above is its actual configuration and its actual output.</p>
<p>We work across all three planes rather than the model call alone for the reason in the middle of this piece: by the time an agent is invoking tools, the model call is the least consequential hop in the chain.</p>
<p>If you can already answer the four questions at the top, you don't need us. If answering them would take a week, the gap is architectural — and it doesn't close by writing it down.</p>]]></content:encoded>
            <category>governance</category>
            <category>architecture</category>
            <category>observability</category>
        </item>
        <item>
            <title><![CDATA[DVARA 1.6.0 — Community Edition, now public]]></title>
            <link>https://dvarahq.com/blog/dvara-1-6-0</link>
            <guid>https://dvarahq.com/blog/dvara-1-6-0</guid>
            <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The free Community Edition is now a separate public image you can pull and run in minutes — with a trust surface, opt-in telemetry, and no lock-in floor.]]></description>
            <content:encoded><![CDATA[<p>DVARA 1.6.0 made the free <strong>Community Edition</strong> a separate image that anyone could pull and evaluate. This post records how that distribution worked when 1.6 shipped; the update below explains what replaced it in 1.8.</p>
<!-- -->
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Updated 13 September 2026</div><div class="admonitionContent_BuS1"><p><strong>This post describes a model DVARA no longer uses.</strong> It is kept as a record of the 1.6.0 release;
what it says about editions was true then and is not true now.</p><p>As of <strong>1.8.0</strong>, DVARA has two distributions. <strong>DVARA Open Source</strong> is the
Apache-2.0 standalone LLM Gateway with file configuration and no Flightdeck.
The <strong>Enterprise platform</strong> adds Flightdeck, central persistence, fleet
operations, advanced LLM capabilities, and the MCP and A2A planes. Enterprise
runs without a key in Development posture; a licence grants production rights
and support and activates MCP and A2A. The current Enterprise images are public,
so no private registry credential or <code>-ee</code> image name is required.</p><p>Licence and posture metadata is available from Enterprise
<code>/actuator/license</code>, not <code>/actuator/gateway-status</code>. The paragraphs below retain
the 1.6.0 Community/private-image topology as release history.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="two-editions-one-download-away">Two editions, one download away<a href="https://dvarahq.com/blog/dvara-1-6-0#two-editions-one-download-away" class="hash-link" aria-label="Direct link to Two editions, one download away" title="Direct link to Two editions, one download away" translate="no">​</a></h2>
<p>There is no separate Community build to hunt for. DVARA decides the edition at startup:</p>
<ul>
<li class=""><strong>A valid, signature-verified license → Enterprise Edition.</strong> The full governance graph activates.</li>
<li class=""><strong>No license, an invalid one, or one expired beyond its 14-day grace → Community Edition.</strong> The gateway boots and passes traffic on the Community feature set instead of failing to start.</li>
</ul>
<p>The current edition is reported as <code>edition</code> (<code>COMMUNITY</code> / <code>ENTERPRISE</code>) on <code>/actuator/gateway-status</code>, so an operator can always confirm which one is running.</p>
<p>What is new is that the free image is now genuinely free of the paid code. The public <code>dvara-llm-gateway</code> and <code>dvara-flightdeck</code> images are a slim build that <strong>leaves out every paid Enterprise engine</strong> — policy, FinOps, signed audit, SSO and security, advanced routing, agentic, and cache. A security team evaluating Community Edition is no longer, byte-for-byte, holding the enterprise code. The full Enterprise build ships as separate <strong>private</strong> images (<code>dvara-llm-gateway-ee</code>, <code>dvara-flightdeck-ee</code>), and the runtime license still gates Enterprise on those.</p>
<p>One thing that does not change by edition: <strong>Postgres is required for every edition.</strong> Community Edition is a persistent, single-environment deployment — not an in-memory or dev-only mode.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-community-edition-includes">What Community Edition includes<a href="https://dvarahq.com/blog/dvara-1-6-0#what-community-edition-includes" class="hash-link" aria-label="Direct link to What Community Edition includes" title="Direct link to What Community Edition includes" translate="no">​</a></h2>
<p>The whole LLM gateway, unrestricted:</p>
<ul>
<li class="">The OpenAI-compatible surface — <code>/v1/chat/completions</code>, <code>/completions</code>, <code>/responses</code>, <code>/embeddings</code>, <code>/models</code>, <code>/batches</code>, <code>/files</code></li>
<li class="">15+ providers, provider switching by model string, default fallback, and the resilience layer (circuit breakers, retries)</li>
<li class="">API-key authentication with workspace plumbing, request and access logging</li>
<li class="">Raw token-usage metering, Prometheus metrics</li>
<li class="">The Console, with built-in username/password login and RBAC roles</li>
</ul>
<p>On top of that, four <strong>scaled-down governance features</strong> so a free install shows what governance actually feels like rather than being a bare proxy:</p>
<ul>
<li class=""><strong>Static model allow/deny list</strong> — a single list, no policy DSL and no dry-run. A blocked model returns <code>POLICY_DENIED</code>.</li>
<li class=""><strong>Local request log</strong> — who, when, which model, how many tokens, viewable in the Console, retained about 7 days. It is <strong>unsigned</strong>: no HMAC, no hash chain, no SIEM export.</li>
<li class=""><strong>Regex-only PII detection</strong> — the built-in patterns (SSN, checksum-validated card numbers), with log or block as the action. No redaction, no tokenization.</li>
<li class=""><strong>Read-only cost estimate</strong> — per-model spend visibility. No budgets, no caps, no enforcement.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-the-line-is">Where the line is<a href="https://dvarahq.com/blog/dvara-1-6-0#where-the-line-is" class="hash-link" aria-label="Direct link to Where the line is" title="Direct link to Where the line is" translate="no">​</a></h2>
<table><thead><tr><th></th><th>Community</th><th>Enterprise</th></tr></thead><tbody><tr><td>LLM gateway, providers, routing, fallback, Console</td><td>Full</td><td>Full</td></tr><tr><td>Policy</td><td>Static model allow/deny list</td><td>Policy-as-code — DSL, versioning, dry-run, shadow, conflict detection</td></tr><tr><td>Audit</td><td>Local, unsigned, ~7-day</td><td>Tamper-evident HMAC hash chain, unlimited retention, SIEM export, compliance reports</td></tr><tr><td>PII</td><td>Regex detect → log or block</td><td>Presidio/GLiNER + composite detection, redaction, reversible tokenization</td></tr><tr><td>Cost</td><td>Read-only estimate</td><td>Budgets, hard caps, per-call gate, chargeback, forecast, model downgrade</td></tr><tr><td>MCP governance plane</td><td>—</td><td>Full governed tool chain</td></tr><tr><td>A2A governance plane</td><td>—</td><td>Full governed hop</td></tr><tr><td>SSO, real rate limiting, semantic cache, advanced routing, Vault/mTLS</td><td>—</td><td>Included</td></tr></tbody></table>
<p>The shape of that split is deliberate. In Community Edition you can <strong>see</strong> your traffic, <strong>catch</strong> an obvious SSN, and <strong>block</strong> an expensive model — enough to feel what governance does. What you cannot do is <strong>prove</strong> any of it to an auditor, <strong>enforce</strong> it richly, or <strong>protect</strong> the data itself. Those are the things a regulated production deployment actually needs, and they are what Enterprise Edition is for.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="start-in-five-minutes">Start in five minutes<a href="https://dvarahq.com/blog/dvara-1-6-0#start-in-five-minutes" class="hash-link" aria-label="Direct link to Start in five minutes" title="Direct link to Start in five minutes" translate="no">​</a></h2>
<p>A single <code>docker run</code> of the public Community Edition image — no API key, no license — takes you from nothing to a first governed request, verified against the published image. A second step shows governance actually doing something: the Community deny-list teaser blocks a request with <code>POLICY_DENIED</code>, so the first thing a new user sees is a policy firing, not a bare proxy. The <a class="" href="https://dvarahq.com/docs/quickstart">quickstart</a> walks the whole path, including how to upgrade to Enterprise later.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-trust-surface-for-a-closed-source-product">A trust surface for a closed-source product<a href="https://dvarahq.com/blog/dvara-1-6-0#a-trust-surface-for-a-closed-source-product" class="hash-link" aria-label="Direct link to A trust surface for a closed-source product" title="Direct link to A trust surface for a closed-source product" translate="no">​</a></h2>
<p>Community Edition is <strong>free, but not open source</strong> — DVARA does not publish its source. So 1.6.0 ships the instrument that stands in place of source access: a responsible-disclosure process — <code>SECURITY.md</code>, with a coordinated-disclosure SLA and safe harbor for good-faith research — backed by a live <code>/.well-known/security.txt</code>.</p>
<p>If you cannot read the code, you can at least see how vulnerabilities are handled.</p>
<p><strong>Opt-in usage telemetry.</strong> Community Edition can now send a small usage signal — <strong>off by default, and Community-Edition only</strong>. A licensed or air-gapped install never sends anything, and not because a switch is set the right way: the telemetry simply isn't active outside Community Edition. When you turn it on, five non-sensitive fields leave the box: an anonymous install id, the edition, the version, a <strong>coarse</strong> request-volume bucket (a range, never an exact count), and a timestamp. Nothing else — no prompts, no responses, no PII, no provider or model names. The full payload is documented field-by-field, so you can see exactly what would be sent before enabling it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="no-lock-in">No lock-in<a href="https://dvarahq.com/blog/dvara-1-6-0#no-lock-in" class="hash-link" aria-label="Direct link to No lock-in" title="Direct link to No lock-in" translate="no">​</a></h2>
<p>Community Edition changes what happens when a paid license lapses, and it is worth being precise.</p>
<p>An Enterprise license that expires gets a <strong>14-day grace period</strong>, fully operational, with warning headers and audit events. After the grace period the data plane returns <code>402 LICENSE_EXPIRED</code> — administrative <strong>reads</strong> stay available so an operator can still diagnose and export configuration. That block is intentional: a paid product should be able to stop on non-payment.</p>
<p>What Community Edition adds is a floor beneath it. An operator who does not want to renew can <strong>drop to the free edition and keep running</strong> — free and unblocked, at the Community feature set. So the honest promise is not "Enterprise never stops." It is <strong>you can always fall back to the free edition and keep serving traffic</strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="turning-on-enterprise">Turning on Enterprise<a href="https://dvarahq.com/blog/dvara-1-6-0#turning-on-enterprise" class="hash-link" aria-label="Direct link to Turning on Enterprise" title="Direct link to Turning on Enterprise" translate="no">​</a></h2>
<p>Because the public base-name images are now slim Community Edition, <strong>turning on Enterprise is a re-image, not a key-swap.</strong> Running Enterprise means pulling the private <code>-ee</code> image with a customer registry credential (<code>docker login ghcr.io</code>) <strong>and</strong> applying a license. Concretely, moving from a free install to a paid one:</p>
<ul>
<li class="">Point your deployment at <code>dvara-llm-gateway-ee</code> / <code>dvara-flightdeck-ee</code> instead of the public names.</li>
<li class="">Add an <code>imagePullSecret</code> (or <code>docker login</code>) for the private registry.</li>
<li class="">Apply your license as before.</li>
</ul>
<p>Existing Enterprise deployments that already pull a full image and apply a license are unaffected — this only changes the base-name images.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="also-in-160--signed-agent-cards-on-the-a2a-plane">Also in 1.6.0 — signed Agent Cards on the A2A plane<a href="https://dvarahq.com/blog/dvara-1-6-0#also-in-160--signed-agent-cards-on-the-a2a-plane" class="hash-link" aria-label="Direct link to Also in 1.6.0 — signed Agent Cards on the A2A plane" title="Direct link to Also in 1.6.0 — signed Agent Cards on the A2A plane" translate="no">​</a></h2>
<p>Alongside the Community Edition work, the agent-to-agent governance plane gets the piece everything downstream depends on: <strong>knowing a peer agent is who it claims to be.</strong></p>
<p>When DVARA discovers a peer agent, it now verifies the cryptographic signature on that agent's Agent Card, following the A2A specification's signed-card format. It surfaces a <strong>trust state</strong> — <code>VERIFIED</code>, <code>SELF_SIGNED</code>, <code>UNSIGNED</code>, or <code>INVALID</code> — on the served card, in the agent registry, and on the discovery audit event, and it enforces it: a signature that fails against the operator-pinned key is rejected outright, and a strict mode (<code>require-signed-cards</code>) refuses anything that is not fully <code>VERIFIED</code>. By default it is fail-soft, so you can adopt it without breaking existing peers. The operator-pinned public key on each agent — set through the Automation API or the Console, with a trust badge on the A2A agents page — is the trust anchor the delegation chain will build on in 1.7.0.</p>
<p>1.6.0 also freezes the design for cryptographic delegation: a signed delegation chain with strict scope-subset attenuation, where a delegated token can only ever <em>narrow</em> authority, never widen it. And two quiet correctness gaps are fixed — skill-scoped policy rules and <code>approval.required-skills</code> now actually govern A2A hops, and a peer-supplied context id is now recorded in per-hop metering instead of coming through empty.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="upgrade">Upgrade<a href="https://dvarahq.com/blog/dvara-1-6-0#upgrade" class="hash-link" aria-label="Direct link to Upgrade" title="Direct link to Upgrade" translate="no">​</a></h2>
<p>1.6.0 is a <strong>plain version bump</strong> over 1.5.0 — same image names, same public/private split, same registry model. Pull the <code>1.6.0</code> images (or the <code>oci://ghcr.io/dvarahq/charts/dvara:1.6.0</code> chart), keep your license and configuration, and roll.</p>
<p>If you are new to DVARA: it is self-managed, activated by a signed license key, with bring-your-own-key credentials so provider keys and request data never leave your perimeter. Start free with the <a class="" href="https://dvarahq.com/docs/quickstart">public Community Edition quickstart</a>, or get a <strong>30-day, all-features trial license</strong> — every feature unlocked, including the MCP and A2A planes. <a class="" href="https://dvarahq.com/pricing">See the editions</a> or, for production licensing or a managed deployment in your region, <a class="" href="https://dvarahq.com/pricing">talk to us</a>.</p>]]></content:encoded>
            <category>governance</category>
            <category>architecture</category>
            <category>a2a</category>
        </item>
        <item>
            <title><![CDATA[What Is Agent-to-Agent (A2A), and How Do You Govern It?]]></title>
            <link>https://dvarahq.com/blog/what-is-agent-to-agent-a2a</link>
            <guid>https://dvarahq.com/blog/what-is-agent-to-agent-a2a</guid>
            <pubDate>Thu, 16 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Agents are starting to call each other. What the agent-to-agent protocol is, why the delegation hop is an ungoverned blind spot, and what governing it takes.]]></description>
            <content:encoded><![CDATA[<p>For two years the story of enterprise AI has been an agent calling a model, and — more recently — an agent calling a tool. There's a third call now, and most teams can't see it: an agent calling <strong>another agent</strong>. That hop has a name — <strong>agent-to-agent (A2A)</strong> — and it is the least-governed surface in the entire AI stack.</p>
<p>This post is a practical definition of A2A: what the protocol actually is, why the delegation hop between two agents is a blind spot your existing controls don't cover, and what it takes to govern it.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-agent-to-agent-a2a">What is agent-to-agent (A2A)?<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#what-is-agent-to-agent-a2a" class="hash-link" aria-label="Direct link to What is agent-to-agent (A2A)?" title="Direct link to What is agent-to-agent (A2A)?" translate="no">​</a></h2>
<p><strong>Agent-to-agent (A2A) is a protocol for one AI agent to delegate work to another.</strong> Instead of a single monolithic agent that does everything, you get specialists: a scheduling agent, a billing agent, a research agent, a customer-record agent. When the scheduling agent needs a customer's billing status, it doesn't reimplement billing — it asks the billing agent. That request, and the reply, is an A2A hop.</p>
<p>The emerging A2A protocol standardizes what that looks like on the wire. A few concepts matter for the rest of this post:</p>
<ul>
<li class=""><strong>Agent Cards.</strong> Each agent publishes a machine-readable description of itself at a well-known discovery endpoint (<code>/.well-known/agent-card.json</code>) — its identity, endpoint, and the <strong>skills</strong> it offers. Other agents read the card to decide who can do what.</li>
<li class=""><strong>Messages and tasks.</strong> A caller sends a message (<code>message/send</code>), optionally streams the response (<code>message/stream</code>), and can manage long-running work through a task lifecycle (<code>tasks/get</code>, <code>list</code>, <code>cancel</code>, <code>resubscribe</code>).</li>
<li class=""><strong>Push notifications.</strong> For work that outlives a single request, a caller can register a webhook so the peer can call it back when a task completes.</li>
<li class=""><strong>Delegated authority.</strong> A hop often carries an <em>on-behalf-of</em> credential — the calling agent is acting for an end user, and that user's authority rides along with the request.</li>
</ul>
<p>None of this is exotic. It's a normal RPC pattern. What makes it dangerous is <em>where</em> it sits.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-a2a-hop-is-a-new-attack-surface">Why the A2A hop is a new attack surface<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#why-the-a2a-hop-is-a-new-attack-surface" class="hash-link" aria-label="Direct link to Why the A2A hop is a new attack surface" title="Direct link to Why the A2A hop is a new attack surface" translate="no">​</a></h2>
<p>Your LLM gateway governs the call from your app to the model. If you've adopted an <a class="" href="https://dvarahq.com/mcp-gateway">MCP Gateway</a>, you also govern the call from your agent to its tools. <strong>The A2A hop is the one in between those two that nobody instruments</strong> — and it is arguably the highest-risk of the three, because a hop to another <em>agent</em> is a hop to something that will itself go make more calls.</p>
<p>Left ungoverned, the delegation hop is invisible in five specific ways:</p>
<ol>
<li class=""><strong>No caller identity.</strong> When Agent A calls Agent B directly, B often has no reliable idea <em>which workspace, which user, which application</em> is really behind the request. Authority gets muddled the moment it's delegated.</li>
<li class=""><strong>No policy on what may be delegated.</strong> There's no control point that says "the scheduling agent may ask the billing agent for <em>status</em>, but never for <em>card numbers</em>." Any agent can ask any peer for any skill it advertises.</li>
<li class=""><strong>No scan of the data that crosses.</strong> A message from one agent to another can carry PII, secrets, or a whole customer record in its payload — and it leaves your perimeter for the peer with nobody looking at it.</li>
<li class=""><strong>No record that it happened.</strong> When an auditor asks "show me every time an agent acted on this customer's behalf," there is no trail. The hop simply isn't logged anywhere that survives.</li>
<li class=""><strong>No brake on runaway delegation.</strong> Agent A asks B, which asks C, which asks A again. Without loop detection across the delegation chain, a multi-agent system can spiral — burning budget and hammering downstream systems — with no kill switch.</li>
</ol>
<p>On top of those, the protocol's own features add sharp edges. <strong>Push-notification webhooks</strong> are a classic SSRF vector — a workspace-supplied callback URL pointed at <code>169.254.169.254</code> or an internal service is a data-exfiltration or metadata-theft primitive if the proxy forwards it blindly. And <strong>Agent Cards</strong> over-advertise by default: an agent that lists every skill it <em>can</em> do, to every caller, is a discovery-time information leak.</p>
<p>This is the same lesson the industry already learned with tools and MCP — an agent will send an SSN to a tool that didn't need it, because models aren't trained to think about data minimization — except now the "tool" is another autonomous agent that will take that data and act on it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-it-means-to-govern-agent-to-agent-calls">What it means to govern agent-to-agent calls<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#what-it-means-to-govern-agent-to-agent-calls" class="hash-link" aria-label="Direct link to What it means to govern agent-to-agent calls" title="Direct link to What it means to govern agent-to-agent calls" translate="no">​</a></h2>
<p><strong>Governing A2A means putting a control point on the delegation hop</strong> — the same move that an LLM Gateway makes for model calls and an MCP Gateway makes for tool calls. Every hop passes through one place that can authenticate it, decide it, scan it, and record it, <em>before</em> the message reaches the peer.</p>
<p>Concretely, agent-to-agent governance is a set of controls on that hop:</p>
<ul>
<li class=""><strong>Caller identity on every hop.</strong> Resolve the calling agent to a real workspace (and, via the delegated on-behalf-of credential, a real end user) before anything else runs. Delegation without identity is the root cause of most of the risks above.</li>
<li class=""><strong>A peer-agent registry with per-workspace scope.</strong> Register each peer agent once — its endpoint, auth scheme, and credentials — then scope <em>which agents and which skills</em> each workspace is allowed to reach. Default-deny, not default-reach.</li>
<li class=""><strong>Policy on every hop.</strong> Allow or deny a hop by caller, target agent, or requested skill, expressed as <a class="" href="https://dvarahq.com/policy-as-code-llm">policy-as-code</a> with dry-run, versioning, and rollback — so "who may delegate what to whom" is a reviewable, testable artifact, not a hard-coded assumption inside an agent.</li>
<li class=""><strong>PII scanning on the message.</strong> Inspect the outbound message — and streamed events — for PII, then block or redact before it crosses to the peer. Data minimization enforced at the boundary, not hoped for.</li>
<li class=""><strong>Human approval gates.</strong> For sensitive hops — a particular skill, a particular target agent — pause and require a human to approve before execution, resolvable from a console or portal.</li>
<li class=""><strong>Delegation-loop detection.</strong> Watch the chain of hops and kill a session that starts to spiral before it runs away.</li>
<li class=""><strong>Governed discovery.</strong> Serve each peer's Agent Card <em>through</em> the control point with a per-workspace skills allow-list, so agents advertise only the skills you intend — closing the over-advertisement leak.</li>
<li class=""><strong>Centralized credentials.</strong> Hold peer-agent credentials centrally — stored, or forwarded on-behalf-of — so calling agents never see raw secrets.</li>
<li class=""><strong>A tamper-evident audit trail.</strong> Sign and hash-chain every hop into an append-only record, so "which agent acted on whose behalf, and what data crossed" is answerable — and provably un-altered.</li>
</ul>
<p>That last one is the point where A2A governance meets compliance. A delegation you can't prove happened is a delegation you can't defend in an audit; a <a class="" href="https://dvarahq.com/ai-audit-compliance">tamper-evident AI audit log</a> of every hop is what turns "our agents talk to each other" into "here is exactly what they did."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-dvara-governs-a2a-a-separate-independent-plane">How DVARA governs A2A: a separate, independent plane<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#how-dvara-governs-a2a-a-separate-independent-plane" class="hash-link" aria-label="Direct link to How DVARA governs A2A: a separate, independent plane" title="Direct link to How DVARA governs A2A: a separate, independent plane" translate="no">​</a></h2>
<p>DVARA treats A2A as a <strong>first-class plane</strong> — a peer to the LLM plane and the MCP plane, not a bolt-on. It has its own governance engines and its own audit chain, so your model and tool traffic are unaffected by what your agents delegate.</p>
<blockquote>
<p><strong>Updated for 1.7.0.</strong> This post originally said the A2A plane ran as its own proxy on <code>:8075</code>. In 1.7.0 the A2A and MCP planes moved in-process — the gateway serves them on its own port, so there is no second service to deploy. What the word "plane" was pointing at is unchanged: separate governance engines, a separate audit chain, and activation only when you licence it. Current documentation calls this component the DVARA A2A Gateway.</p>
</blockquote>
<blockquote>
<p><strong>Updated for 1.8.2.</strong> This post and the note above originally said the A2A plane activates only under a licence. In 1.8.2 every feature, the MCP and A2A planes included, runs without a licence for non-production use, within 3 workspaces and 100,000 governed calls a month. A licence removes both limits and gives production rights and support. An expired licence refuses no traffic.</p>
</blockquote>
<p>The hop flow is deliberately simple: DVARA governs <strong>exactly one hop</strong>, returns the peer's reply unchanged, and makes no onward call of its own.</p>
<ol>
<li class=""><strong>Agent issues a hop.</strong> Your agent sends its A2A message to a peer <em>through</em> the DVARA A2A Gateway instead of calling the peer directly.</li>
<li class=""><strong>Identity &amp; registry.</strong> The proxy authenticates the caller — API key to workspace — and resolves the target agent from that workspace's registry.</li>
<li class=""><strong>Policy, PII &amp; approval.</strong> The hop is checked against A2A policy, the message is scanned for PII, sensitive hops pause for human approval, and loop detection guards the delegation chain.</li>
<li class=""><strong>Forward &amp; audit.</strong> The message reaches the peer, the reply is returned unchanged, and every step is signed into the A2A audit chain and metered per hop.</li>
</ol>
<p>Coverage spans the whole protocol — <code>message/send</code>, streaming <code>message/stream</code>, the task lifecycle, and push-notification config — with the workspace-supplied webhook URLs SSRF-validated before they're ever forwarded. The A2A audit chain is its own append-only, HMAC-signed, hash-chained trail, separate from the LLM/MCP chain but unioned into the same SIEM exports and compliance reports.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-a2a-fits-the-three-planes-of-ai-governance">Where A2A fits: the three planes of AI governance<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#where-a2a-fits-the-three-planes-of-ai-governance" class="hash-link" aria-label="Direct link to Where A2A fits: the three planes of AI governance" title="Direct link to Where A2A fits: the three planes of AI governance" translate="no">​</a></h2>
<p>The shape here is not new — it's the <em>third instance</em> of the same pattern. Governance belongs at the boundary every request already crosses:</p>
<ul>
<li class=""><strong>The LLM plane</strong> governs your app-to-model calls — routing, <a class="" href="https://dvarahq.com/policy-as-code-llm">policy-as-code</a>, PII, cost, and audit on every completion.</li>
<li class=""><strong>The MCP plane</strong> governs your agent-to-tool calls — <a class="" href="https://dvarahq.com/mcp-gateway">MCP tool-call governance</a> with per-tool policy, PII scanning, approval gates, and audit.</li>
<li class=""><strong>The A2A plane</strong> governs your agent-to-agent calls — <a class="" href="https://dvarahq.com/a2a-governance">agent-to-agent governance</a> with identity, per-hop policy, PII, approval, loop detection, and its own audit chain.</li>
</ul>
<p>Model, tool, agent. Three call types, one governance model, one tamper-evident audit story across all three. If your agents already call models and tools through a governed boundary, extending that boundary to the hops they make to each other is the natural next step — and it's the one that most teams haven't taken yet.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="frequently-asked-questions">Frequently asked questions<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#frequently-asked-questions" class="hash-link" aria-label="Direct link to Frequently asked questions" title="Direct link to Frequently asked questions" translate="no">​</a></h2>
<p><strong>What does A2A stand for?</strong> Agent-to-agent — a protocol for one AI agent to delegate work to another, so specialist agents can call each other instead of one monolithic agent doing everything.</p>
<p><strong>How is A2A different from MCP?</strong> MCP (Model Context Protocol) governs an agent calling a <strong>tool</strong> — a database, an API, a file store. A2A governs an agent calling <strong>another agent</strong>. They're complementary: an agent might use MCP to read a database and A2A to ask a billing agent for a customer's status. The risk profile differs too — a tool executes and returns, but a peer agent may itself make more calls, so A2A carries delegation-chain risk that tools don't.</p>
<p><strong>Why is agent-to-agent a security risk?</strong> The hop between two agents is usually invisible: no reliable caller identity, no policy on what may be delegated, no scan of the data crossing to the peer, and no record it happened. Add runaway delegation loops, SSRF-prone push webhooks, and over-advertised Agent Cards, and the delegation hop becomes the least-instrumented, highest-blast-radius surface in the stack.</p>
<p><strong>What does it take to govern A2A?</strong> A control point on the hop that authenticates the caller, resolves the peer from a scoped registry, evaluates the hop against policy, scans the message for PII, optionally requires human approval, detects delegation loops, and signs every hop into a tamper-evident audit trail — before the message reaches the peer.</p>
<p><strong>Do I need A2A governance if I already govern LLM and MCP calls?</strong> If your agents delegate to one another, yes — it's the third of the same three call types (model, tool, agent) and the one most teams haven't instrumented yet. The controls are cheapest to add before the delegation graph gets complex.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started">Getting started<a href="https://dvarahq.com/blog/what-is-agent-to-agent-a2a#getting-started" class="hash-link" aria-label="Direct link to Getting started" title="Direct link to Getting started" translate="no">​</a></h2>
<p>A2A is early, which is exactly why it's worth governing now: the controls are cheapest to put in before the delegation graph gets complicated, not after. If your agents are starting to delegate to one another, the fastest path is to read how the <a class="" href="https://dvarahq.com/a2a-governance">A2A governance plane</a> works, or go straight to the <a class="" href="https://dvarahq.com/docs/native-a2a">A2A Governance Plane documentation</a> for the concrete registry, policy, and audit model.</p>
<p>The delegation hop between two agents is invisible today. It doesn't have to stay that way.</p>]]></content:encoded>
            <category>governance</category>
            <category>agentic-ai</category>
            <category>security</category>
            <category>ai-safety</category>
        </item>
        <item>
            <title><![CDATA[DVARA 1.3.0 — The A2A Governance Plane]]></title>
            <link>https://dvarahq.com/blog/dvara-1-3-0</link>
            <guid>https://dvarahq.com/blog/dvara-1-3-0</guid>
            <pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DVARA 1.3.0 adds a first-class Agent-to-Agent governance plane — authenticated, policy-checked, PII-scanned and tamper-evidently audited agent-to-agent hops.]]></description>
            <content:encoded><![CDATA[<p>DVARA 1.3.0 adds a <strong>third governance plane</strong>: agent-to-agent. It puts identity, policy, PII checks, and tamper-evident audit on the hop from one agent to another, alongside the existing LLM and MCP planes.</p>
<p>Everything below ships in every Enterprise install — the same governance core across all three planes. 1.3.0 is a drop-in upgrade from 1.2.x.</p>
<!-- -->
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Updated 13 September 2026</div><div class="admonitionContent_BuS1"><p>This post describes 1.3.0 as it shipped. DVARA 1.8 has separate Open Source and
Enterprise distributions. The MCP and A2A planes are part of the Enterprise
platform and require a licence to activate; Enterprise LLM governance runs
without a key in Development posture. Current documentation calls these
components the <strong>DVARA MCP Gateway</strong> and <strong>DVARA A2A Gateway</strong>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="govern-the-hop-not-the-orchestration">Govern the hop, not the orchestration<a href="https://dvarahq.com/blog/dvara-1-3-0#govern-the-hop-not-the-orchestration" class="hash-link" aria-label="Direct link to Govern the hop, not the orchestration" title="Direct link to Govern the hop, not the orchestration" translate="no">​</a></h2>
<p>An agent calling another agent is a delegation: agent A asks agent B to do something on a user's behalf. Left ungoverned, that hop is invisible — no identity on the caller, no policy on what may be delegated, no scan of what data crosses, no record that it happened.</p>
<p>DVARA's A2A plane governs exactly one hop at a time. A request comes in for agent B, the plane runs the full governance chain, forwards it to B, and returns B's reply. It makes <strong>no onward call</strong> and runs <strong>no agent loop</strong> — orchestration stays where it belongs, in your framework. DVARA is the checkpoint each hop passes through, not the conductor.</p>
<p>The plane is <strong>fully independent</strong> of the LLM and MCP planes — its own proxy (<code>dvara-a2a-gateway</code>, port 8075), its own governance engines, its own audit chain. It ships as an <strong>opt-in</strong> component, off by default, so existing three-plane deployments are untouched until you turn it on.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-a-governed-hop-checks">What a governed hop checks<a href="https://dvarahq.com/blog/dvara-1-3-0#what-a-governed-hop-checks" class="hash-link" aria-label="Direct link to What a governed hop checks" title="Direct link to What a governed hop checks" translate="no">​</a></h2>
<p>Every A2A hop runs through a chain of operator-controlled checks:</p>
<ul>
<li class=""><strong>Caller identity</strong> — the calling agent presents a DVARA API key, resolved to a workspace. No anonymous hops.</li>
<li class=""><strong>Registered peers</strong> — agent B must be a peer you registered, with its endpoint, auth scheme, and credentials under your control. Callers reference agents by id, never by raw URL.</li>
<li class=""><strong>A2A policy</strong> — an operator-authored policy DSL decides whether A may call B, or a given skill on B. Versioned, with lifecycle and rollback, like the LLM and MCP policy engines — but its own rules, in its own store.</li>
<li class=""><strong>PII</strong> — the outbound message is scanned; PII is redacted in flight or the hop is blocked, per your action.</li>
<li class=""><strong>Loop detection</strong> — runaway delegation chains are detected and the session killed before they spiral.</li>
<li class=""><strong>Human approval</strong> — hops matching a workspace's rules (by skill or by target agent) pause on a <strong>durable, cross-pod</strong> approval gate; a reviewer approves or denies from the Console or the workspace Portal, and the paused hop releases on whichever pod is holding it.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="governed-across-the-full-a2a-surface">Governed across the full A2A surface<a href="https://dvarahq.com/blog/dvara-1-3-0#governed-across-the-full-a2a-surface" class="hash-link" aria-label="Direct link to Governed across the full A2A surface" title="Direct link to Governed across the full A2A surface" translate="no">​</a></h2>
<p>1.3.0 governs the whole protocol surface, not just the basic send:</p>
<ul>
<li class=""><strong><code>message:send</code></strong> — the governed synchronous hop.</li>
<li class=""><strong><code>message:stream</code></strong> — streaming hops with <strong>in-flight PII enforcement</strong> over the SSE events.</li>
<li class=""><strong><code>tasks:get · list · cancel · resubscribe</code></strong> — the task lifecycle, with PII-scanned task reads.</li>
<li class=""><strong><code>pushNotificationConfig</code> CRUD</strong> — push-webhook config, with the workspace-supplied callback URL <strong>SSRF-validated</strong> before it's ever forwarded.</li>
<li class=""><strong>Agent Card discovery</strong> — <code>/.well-known/agent-card.json</code>, served through DVARA with a per-workspace <strong>skills allow-list</strong> so you advertise only the skills you intend to expose.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-separate-plane-means-a-separate-record">A separate plane means a separate record<a href="https://dvarahq.com/blog/dvara-1-3-0#a-separate-plane-means-a-separate-record" class="hash-link" aria-label="Direct link to A separate plane means a separate record" title="Direct link to A separate plane means a separate record" translate="no">​</a></h2>
<p>The A2A plane writes its own <strong>tamper-evident, HMAC hash-chained audit trail</strong> — every hop intent and result, every policy denial, PII action, loop kill, and approval decision — plus per-hop metering. Because it uses the same signing primitive as the LLM and MCP chains, that A2A trail folds straight into what you already run: it exports to <strong>Splunk, CloudWatch, and Kafka</strong> alongside the rest, and it shows up in the <strong>SOC 2, HIPAA, GDPR, and India RBI/SEBI</strong> compliance reports.</p>
<p>A note on scope. In 1.3.0, DVARA authenticates the caller, enforces the agents and policy you registered, and <strong>records every delegation tamper-evidently</strong> — including on-behalf-of tokens, which are forwarded to the peer for it to enforce. Cryptographic verification of the authority to delegate — signed, portable delegation with scope attenuation — is the next step on the roadmap, not a claim we make today.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="console-review--pagination-navigation-and-sessions">Console review — pagination, navigation, and sessions<a href="https://dvarahq.com/blog/dvara-1-3-0#console-review--pagination-navigation-and-sessions" class="hash-link" aria-label="Direct link to Console review — pagination, navigation, and sessions" title="Direct link to Console review — pagination, navigation, and sessions" translate="no">​</a></h2>
<p>1.3.0 also lands a Console review sweep:</p>
<ul>
<li class=""><strong>Server-side pagination</strong> across the high-volume views — token usage, audit (both the shared and the A2A chains), MCP tool calls, the cost dashboard, and the agent-session viewer — so large installs page instead of loading everything.</li>
<li class=""><strong>A reorganized navigation</strong> grouped into task-oriented sections, with the <strong>audit viewer and the approval queue unified behind a plane selector</strong> so LLM, MCP, and A2A share one surface.</li>
<li class=""><strong>A longer, verified session</strong> — the Console session lifetime is now four hours, backed by durable JDBC-stored sessions, ending the frequent re-logins.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="upgrade">Upgrade<a href="https://dvarahq.com/blog/dvara-1-3-0#upgrade" class="hash-link" aria-label="Direct link to Upgrade" title="Direct link to Upgrade" translate="no">​</a></h2>
<p>1.3.0 is a drop-in upgrade over 1.2.x. Pull the <code>1.3.0</code> images (or the <code>oci://ghcr.io/dvarahq/charts/dvara:1.3.0</code> chart), keep your existing license and configuration, and roll — the A2A schema migrations apply automatically on first boot.</p>
<p>The A2A plane is <strong>opt-in</strong>. Turn it on by enabling the <code>dvara-a2a-gateway</code> component (chart) or adding the service (Compose); leave it off and nothing changes. See <a class="" href="https://dvarahq.com/docs/native-a2a">the A2A Governance Plane docs</a> for the full surface — the governance chain, the agent registry, and the documented limits.</p>
<p>If you are new to DVARA: it is self-managed, activated by a signed license key, with bring-your-own-key credentials so provider keys and request data never leave your perimeter. Start with a <strong>30-day, all-features trial license</strong> — every feature unlocked, including native MCP and the A2A plane, no credit card. <a class="" href="https://dvarahq.com/pricing">Get the trial</a> or, for production licensing or a managed deployment in your region, <a class="" href="https://dvarahq.com/pricing">talk to us</a>. The release is live now.</p>]]></content:encoded>
            <category>governance</category>
            <category>agent-to-agent</category>
        </item>
        <item>
            <title><![CDATA[How to Compare LLM Gateways]]></title>
            <link>https://dvarahq.com/blog/llm-gateway-alternatives</link>
            <guid>https://dvarahq.com/blog/llm-gateway-alternatives</guid>
            <pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A framework for comparing LLM gateways governance-first — the categories on the market, the criteria that matter, and how to score them on your constraints.]]></description>
            <content:encoded><![CDATA[<p>"Which LLM gateway should we use?" doesn't have a universal answer, because teams optimize for different things — fastest to adopt, cheapest to run, most observable, or most governable. What <em>is</em> universal is the set of questions worth asking, and the order to ask them in.</p>
<p>This is a framework, not a leaderboard. And it's deliberately <strong>governance-first</strong>, because that's the axis where products differ most and demo videos reveal least. Routing and provider coverage are easy to compare and roughly commoditized; whether a platform can <em>enforce</em> a policy, <em>prove</em> what it did, and <em>keep sensitive data in your perimeter</em> is where the real divergence — and the real buying decision — lives. DVARA is one option in the field; the point of this post is to help you score the field, including us, on criteria that matter.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-categories-on-the-market">The categories on the market<a href="https://dvarahq.com/blog/llm-gateway-alternatives#the-categories-on-the-market" class="hash-link" aria-label="Direct link to The categories on the market" title="Direct link to The categories on the market" translate="no">​</a></h2>
<p>Broadly, the options fall into a few shapes, each with a different center of gravity:</p>
<ul>
<li class=""><strong>Open-source proxies / libraries</strong> (e.g. LiteLLM, Bifrost). Fast to start, free to run, huge provider coverage. You operate and secure them yourself, and governance depth — Policy-as-Code, tamper-evident audit, PII enforcement — is usually thin or DIY. Great when routing and provider abstraction are the whole ask.</li>
<li class=""><strong>Cloud-vendor gateways</strong> (e.g. Cloudflare AI Gateway, Kong's AI Gateway add-on). If you're all-in on one ecosystem, the native integration is tight. The trade-off is gravity toward that vendor and weaker neutrality — and governance that's often coarse.</li>
<li class=""><strong>Developer/observability-first platforms</strong> (e.g. Portkey, Helicone). Strong on tracing, evals, and prompt management, with a gateway attached. <em>(Since this was written, Helicone was acquired by Mintlify in March 2026 and has moved to maintenance mode, with its experiments feature withdrawn — read it as a description of the category rather than a current recommendation.)</em> A good fit if your primary pain is <em>visibility</em>; typically lighter on hard enforcement (blocking budget caps, policy denial, PII blocking) and on self-hosting for regulated data.</li>
<li class=""><strong>Governance-native platforms.</strong> Built around Policy-as-Code, PII/guardrails, tamper-evident audit, agentic (MCP) governance, and self-hosting from the start, with routing and cost as table stakes. The fit when compliance and control are the point. <strong>DVARA sits here</strong> — and the honest framing is that most of the field started as a gateway and added governance, while a governance-native platform starts from the control requirements and ships the gateway as the mechanism.</li>
</ul>
<p>No category is "best." They're answers to different questions.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Correction, 2026-08-28</div><div class="admonitionContent_BuS1"><p>This post previously described DVARA's audit trail as <strong>immutable</strong>, including in the comparison
table below. That was wrong when it was written, and it is corrected throughout.</p><p>The mechanism is HMAC-SHA256 signing plus hash chaining, which makes alteration and removal
<strong>detectable</strong> — it does not prevent them. The trail is append-only <em>through the API</em>; anyone with
direct database access can still delete a row, and what the chain guarantees is that you will know.
That stronger word would be a claim about storage and permissions, which is your deployment rather
than our product. The correct word, used here now, is <strong>tamper-evident</strong>.</p><p>It mattered most in a comparison, which is why the note is on this post: a criterion you are asked
to judge other products against has to be stated accurately about ours first.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-criteria-that-matter--governance-first">The criteria that matter — governance first<a href="https://dvarahq.com/blog/llm-gateway-alternatives#the-criteria-that-matter--governance-first" class="hash-link" aria-label="Direct link to The criteria that matter — governance first" title="Direct link to The criteria that matter — governance first" translate="no">​</a></h2>
<p>Score any candidate on these, weighted by what your team is actually optimizing for. Governance rows lead deliberately:</p>
<table><thead><tr><th>Criterion</th><th>What to ask</th></tr></thead><tbody><tr><td><strong>Policy enforcement</strong></td><td>Policy-as-Code that can <strong>deny</strong> a request (model, tools, region, budget), or only observe?</td></tr><tr><td><strong>Audit</strong></td><td>Tamper-evident trail of every decision — or just request logs?</td></tr><tr><td><strong>PII / guardrails</strong></td><td>Prompt &amp; response scanning that can <strong>block or redact</strong>, in-perimeter?</td></tr><tr><td><strong>Agentic (MCP) governance</strong></td><td>Can it govern <em>tool</em> calls — approval gates, loop detection, tool-poisoning defense — not just model calls?</td></tr><tr><td><strong>Data residency / self-hosting</strong></td><td>Runs <strong>inside your perimeter</strong>, or SaaS-only?</td></tr><tr><td><strong>Cost control</strong></td><td>Real-time attribution and <strong>hard</strong> budget caps, or dashboards only?</td></tr><tr><td><strong>Caching</strong></td><td>Semantic caching, and does a hit cost <strong>$0</strong> and stay PII-safe?</td></tr><tr><td><strong>Routing &amp; failover</strong></td><td>Model-prefix, weighted, and <strong>capability-aware</strong> failover — or blind retries?</td></tr><tr><td><strong>Provider coverage</strong></td><td>The models you use <em>and</em> the ones you might switch to?</td></tr><tr><td><strong>Observability</strong></td><td>Prometheus + OpenTelemetry (incl. GenAI conventions), or a closed dashboard?</td></tr></tbody></table>
<p>A few deserve emphasis because they're where products quietly differ:</p>
<ul>
<li class=""><strong>Enforcement vs. observation.</strong> "We show you your spend / your prompts / your traces" is not "we can <em>stop</em> a disallowed call." Hard caps that block and policies that deny are a different capability class than dashboards that inform. → <strong><a class="" href="https://dvarahq.com/blog/llm-cost-management">Cost Management</a></strong></li>
<li class=""><strong>Where it runs.</strong> SaaS is fastest; self-hosting keeps prompts, policy enforcement, and audit in your perimeter. For regulated data this is often the first filter, not the last. → <strong><a class="" href="https://dvarahq.com/blog/self-hosted-llm-gateway">Self-Hosted LLM Gateway</a></strong></li>
<li class=""><strong>Model-aware governance.</strong> A repurposed API gateway can't see models, tokens, or PII. If governance matters, that gap is disqualifying. → <strong><a class="" href="https://dvarahq.com/blog/why-ai-gateway-not-api-gateway">Why an AI Gateway, Not an API Gateway</a></strong></li>
<li class=""><strong>Capability-aware failover.</strong> Blind failover can retry on a model that can't serve the request. Ask specifically. → <strong><a class="" href="https://dvarahq.com/blog/llm-fallback-failover">Fallback &amp; Failover</a></strong></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-note-on-mcp-and-agents">A note on MCP and agents<a href="https://dvarahq.com/blog/llm-gateway-alternatives#a-note-on-mcp-and-agents" class="hash-link" aria-label="Direct link to A note on MCP and agents" title="Direct link to A note on MCP and agents" translate="no">​</a></h2>
<p>By 2026, governing <em>agent</em> traffic — not just model calls but the tool calls agents make via the Model Context Protocol — is a real axis of comparison, and <strong>most serious gateways now offer some MCP support</strong>. The differentiator isn't presence, it's depth: whether MCP is <em>governed</em> (approval gates, loop detection, tool-poisoning defense, per-call audit) or just proxied. If you're building agents, weight this heavily; if you're not yet, treat it as forward-looking rather than deciding.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-actually-choose">How to actually choose<a href="https://dvarahq.com/blog/llm-gateway-alternatives#how-to-actually-choose" class="hash-link" aria-label="Direct link to How to actually choose" title="Direct link to How to actually choose" translate="no">​</a></h2>
<ol>
<li class=""><strong>Write down your top three constraints.</strong> Regulated data? Multi-cloud? Tiny ops team? These pick your category before you compare products.</li>
<li class=""><strong>Disqualify on the hard requirements.</strong> SaaS-only is a non-starter for some; that filter shrinks the list fast.</li>
<li class=""><strong>Trial two candidates on real traffic.</strong> Routing and coverage demo easily; enforcement, PII blocking, audit integrity, and self-hosting only reveal themselves under load.</li>
<li class=""><strong>Weight enforcement over dashboards</strong> if control is why you're here.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/llm-gateway-alternatives#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p><strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA</a></strong> is an AI gateway that governs model, tool and agent-to-agent calls and deploys anywhere: Policy-as-Code that denies, tamper-evident HMAC-signed audit, PII redaction and guardrails, governed MCP for agent traffic, and data residency by self-hosting — with capability-aware routing and failover, real-time cost attribution and hard budget caps, and $0 PII-safe semantic caching as table stakes. It's the right fit when compliance, control, and sovereignty lead your criteria; if your top constraint is "free and OSS with the widest provider list," an open-source proxy may serve you better. Score it against your own weighted criteria using the pillar guide, <strong><a class="" href="https://dvarahq.com/llm-gateway">What Is an LLM Gateway?</a></strong>, as the reference.</p>]]></content:encoded>
            <category>governance</category>
            <category>architecture</category>
        </item>
        <item>
            <title><![CDATA[LLM Observability: Metrics, Traces and gen_ai Spans]]></title>
            <link>https://dvarahq.com/blog/llm-observability</link>
            <guid>https://dvarahq.com/blog/llm-observability</guid>
            <pubDate>Fri, 03 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[LLM observability for an AI gateway: Prometheus metrics, OpenTelemetry traces with gen_ai span attributes, and per-request telemetry for policy and cost.]]></description>
            <content:encoded><![CDATA[<p>Ask a team running AI in production a few basic questions — <em>what's our p95 latency to Anthropic? how many tokens did the support team burn last week? what's our error rate on <code>gpt-4o</code>?</em> — and watch how long the answer takes. If it's a research project rather than a dashboard glance, you don't have <strong>LLM observability</strong>.</p>
<p>Observability isn't a nice-to-have next to governance; it's the layer <em>underneath</em> it. Policy enforcement, cost attribution, and audit are only as trustworthy as the telemetry that proves they ran. "We block disallowed models" needs a metric that shows the blocks; "we attribute every dollar" needs token counts per workspace; "we can prove what happened" needs traces and logs. In DVARA, the <strong>DVARA LLM Gateway</strong> emits that evidence on every request, which is what turns governance claims into governance you can audit.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-llm-observability-is">What LLM observability is<a href="https://dvarahq.com/blog/llm-observability#what-llm-observability-is" class="hash-link" aria-label="Direct link to What LLM observability is" title="Direct link to What LLM observability is" translate="no">​</a></h2>
<p>LLM observability is the practice of instrumenting your model traffic so you can measure and diagnose it: request rate, latency distribution, token consumption, cost, cache behavior, and errors — broken down by the dimensions that matter (model, provider, workspace, status). It's ordinary observability applied to a new kind of traffic, plus a few AI-specific signals (tokens, cache hits, provider failovers) that generic app monitoring doesn't capture.</p>
<p>Because a governed control point sits on the path of every call, it's the natural place to emit this telemetry consistently — every request, every provider, one schema.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-three-pillars-applied-to-llms">The three pillars, applied to LLMs<a href="https://dvarahq.com/blog/llm-observability#the-three-pillars-applied-to-llms" class="hash-link" aria-label="Direct link to The three pillars, applied to LLMs" title="Direct link to The three pillars, applied to LLMs" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="metrics">Metrics<a href="https://dvarahq.com/blog/llm-observability#metrics" class="hash-link" aria-label="Direct link to Metrics" title="Direct link to Metrics" translate="no">​</a></h3>
<p>The counters and histograms you actually watch on a dashboard:</p>
<ul>
<li class=""><strong>Request rate and latency</strong> — requests/sec and p50/p95/p99 latency, tagged by model, provider, and workspace.</li>
<li class=""><strong>Token usage</strong> — input and output tokens, per model and workspace. This is the raw material for <strong><a class="" href="https://dvarahq.com/blog/llm-cost-management">cost governance</a></strong>.</li>
<li class=""><strong>Errors</strong> — provider errors by code, so you see <em>which</em> provider is failing and <em>how</em>.</li>
<li class=""><strong>Resilience signals</strong> — retries, fallbacks (from-provider → to-provider), and rate-limit sheds, which tell you whether your <strong><a class="" href="https://dvarahq.com/blog/llm-fallback-failover">failover</a></strong> is a quiet safety net or a daily tax.</li>
</ul>
<p>Exposed in a standard format (Prometheus), these drop straight into the monitoring stack you already run.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="traces">Traces<a href="https://dvarahq.com/blog/llm-observability#traces" class="hash-link" aria-label="Direct link to Traces" title="Direct link to Traces" translate="no">​</a></h3>
<p>A single request often fans out — filter chain, routing, an upstream call, maybe a failover to a second provider. <strong>Distributed tracing</strong> stitches those into one timeline so you can see where the milliseconds and the failures actually went, with trace context propagated to the upstream call.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="logs">Logs<a href="https://dvarahq.com/blog/llm-observability#logs" class="hash-link" aria-label="Direct link to Logs" title="Direct link to Logs" translate="no">​</a></h3>
<p>One <strong>structured log line per request</strong> — trace ID, model, provider, status, latency, token counts, workspace, masked API key, cache status, error code — is the record you grep when something specific went wrong. Structured (JSON), not prose, so it's queryable.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-genai-semantic-conventions">The GenAI semantic conventions<a href="https://dvarahq.com/blog/llm-observability#the-genai-semantic-conventions" class="hash-link" aria-label="Direct link to The GenAI semantic conventions" title="Direct link to The GenAI semantic conventions" translate="no">​</a></h2>
<p>Here's the piece that's new and worth understanding. OpenTelemetry has standardized a set of <strong>GenAI semantic conventions</strong> — a common vocabulary for model telemetry, with attributes like <code>gen_ai.system</code> (which provider), <code>gen_ai.request.model</code>, <code>gen_ai.usage.input_tokens</code>, <code>gen_ai.usage.output_tokens</code>, and <code>gen_ai.response.model</code>.</p>
<p>By default, the DVARA LLM Gateway sets these ten on the span for every provider call (<code>management.tracing.genai-semconv.enabled</code> turns them off):</p>
<table><thead><tr><th>Attribute</th><th>What it records</th></tr></thead><tbody><tr><td><code>gen_ai.system</code></td><td>The provider family, such as <code>openai</code> or <code>anthropic</code></td></tr><tr><td><code>gen_ai.operation.name</code></td><td>The kind of call: <code>chat</code> or <code>embeddings</code></td></tr><tr><td><code>gen_ai.request.model</code></td><td>The model the request asked for</td></tr><tr><td><code>gen_ai.request.max_tokens</code></td><td>The output-token limit on the request</td></tr><tr><td><code>gen_ai.request.temperature</code></td><td>The sampling temperature on the request</td></tr><tr><td><code>gen_ai.response.model</code></td><td>The model that actually answered</td></tr><tr><td><code>gen_ai.response.id</code></td><td>The provider's response id</td></tr><tr><td><code>gen_ai.response.finish_reasons</code></td><td>Why generation stopped</td></tr><tr><td><code>gen_ai.usage.input_tokens</code></td><td>Input tokens used</td></tr><tr><td><code>gen_ai.usage.output_tokens</code></td><td>Output tokens used</td></tr></tbody></table>
<p><strong>Which observability platforms support <code>gen_ai</code> span attributes?</strong> Any OpenTelemetry backend stores them as ordinary span attributes. GenAI-aware backends go further and read them as model calls, with tokens and models broken out.</p>
<p>Why it matters: if your platform emits these standard attributes on its spans, your traces are <strong>portable</strong>. GenAI-aware backends — Langfuse, Arize Phoenix, and others — understand them without custom parsing. You're not locked into one vendor's bespoke schema; you're speaking the ecosystem's shared language. The DVARA LLM Gateway emits <code>gen_ai.*</code> alongside its own span attributes, so your governance telemetry travels to whatever backend you standardize on.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-to-actually-put-on-the-dashboard">What to actually put on the dashboard<a href="https://dvarahq.com/blog/llm-observability#what-to-actually-put-on-the-dashboard" class="hash-link" aria-label="Direct link to What to actually put on the dashboard" title="Direct link to What to actually put on the dashboard" translate="no">​</a></h2>
<ul>
<li class=""><strong>Traffic:</strong> requests/sec by model and workspace.</li>
<li class=""><strong>Latency:</strong> p95 by provider (where failover decisions get validated).</li>
<li class=""><strong>Tokens:</strong> input/output per workspace per day (the cost driver).</li>
<li class=""><strong>Errors:</strong> provider error rate by code.</li>
<li class=""><strong>Resilience:</strong> fallback and retry rates by provider pair.</li>
<li class=""><strong>Cache:</strong> hit rate (ties to cost).</li>
</ul>
<p>For the request-level view — streaming, structured outputs, and per-call telemetry together — see the deep dive on <strong><a class="" href="https://dvarahq.com/blog/beyond-basics-streaming-structured-outputs-observability">streaming, structured outputs &amp; observability</a></strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="an-evaluation-checklist">An evaluation checklist<a href="https://dvarahq.com/blog/llm-observability#an-evaluation-checklist" class="hash-link" aria-label="Direct link to An evaluation checklist" title="Direct link to An evaluation checklist" translate="no">​</a></h2>
<ul>
<li class="">Are <strong>Prometheus metrics</strong> exposed for rate, latency, tokens, and errors — tagged by model/provider/workspace?</li>
<li class="">Is there <strong>distributed tracing</strong> with context propagated to upstream calls?</li>
<li class="">Does it emit the <strong>OpenTelemetry GenAI conventions</strong> (<code>gen_ai.*</code>), so traces are portable?</li>
<li class="">Is there <strong>one structured log line per request</strong> with the fields you'd need to debug?</li>
<li class="">Are <strong>resilience and cache</strong> signals (retries, fallbacks, hit rate) observable, not just request/latency?</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/llm-observability#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p><strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA</a></strong> is an AI gateway that governs model, tool and agent-to-agent calls, and observability is the evidence layer beneath it: its LLM Gateway exposes Prometheus metrics for request rate, latency percentiles, token usage, cost, and errors — tagged by workspace, model, and provider — and emits OpenTelemetry traces carrying the standard GenAI semantic-convention attributes (<code>gen_ai.*</code>) so they're portable, alongside one structured JSON log line per request. That telemetry is what lets you <em>prove</em> policy, cost, and audit ran. Read the pillar guide, <strong><a class="" href="https://dvarahq.com/llm-gateway">What Is an LLM Gateway?</a></strong>, for how observability fits the rest.</p>]]></content:encoded>
            <category>governance</category>
            <category>observability</category>
        </item>
        <item>
            <title><![CDATA[Semantic Caching for LLMs Without Storing Sensitive Data]]></title>
            <link>https://dvarahq.com/blog/semantic-caching-llms</link>
            <guid>https://dvarahq.com/blog/semantic-caching-llms</guid>
            <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Semantic caching serves a cached answer to a prompt that means the same thing. Inside DVARA, the AI gateway, PII is stripped before anything is cached.]]></description>
            <content:encoded><![CDATA[<p>Traditional caching keys on an exact match: same input, same cached output. That works for HTTP and falls apart for language, because two prompts can be worded completely differently and mean exactly the same thing. <em>"What's your refund policy?"</em> and <em>"How do I get my money back?"</em> would miss an exact-match cache every time — and you'd pay to generate the same answer twice.</p>
<p><strong>Semantic caching</strong> keys on <em>meaning</em> instead of <em>bytes</em>. But a cache that stores prompts and responses is also a governance surface: it can quietly hold raw PII, and it can quietly keep charging you for calls that never happened. Both are failure modes a governed gateway has to close by default — which is why, in DVARA, <a class="" href="https://dvarahq.com/docs/caching">the semantic cache</a> lives inside the same governed path as Policy-as-Code and PII redaction, not off to the side.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-semantic-caching-is">What semantic caching is<a href="https://dvarahq.com/blog/semantic-caching-llms#what-semantic-caching-is" class="hash-link" aria-label="Direct link to What semantic caching is" title="Direct link to What semantic caching is" translate="no">​</a></h2>
<p>Semantic caching stores prompt–response pairs and serves a cached response when a new prompt is <em>semantically similar</em> to a stored one — close in meaning, not identical in text. Instead of hashing the prompt, it embeds the prompt into a vector and compares that vector against cached prompts by cosine similarity. If the closest match is above a similarity threshold, it's a hit and you return the stored answer. Below the threshold, it's a miss and the call goes to the model.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-it-works-step-by-step">How it works, step by step<a href="https://dvarahq.com/blog/semantic-caching-llms#how-it-works-step-by-step" class="hash-link" aria-label="Direct link to How it works, step by step" title="Direct link to How it works, step by step" translate="no">​</a></h2>
<ol>
<li class=""><strong>Embed the incoming prompt</strong> into a vector.</li>
<li class=""><strong>Compare</strong> it against the vectors of cached prompts (cosine similarity).</li>
<li class=""><strong>Threshold</strong> — if the best match scores above your similarity threshold (say 0.9), serve the cached response.</li>
<li class=""><strong>On a miss</strong>, call the model, then store the new prompt vector and response for next time.</li>
</ol>
<p>The similarity threshold is the dial that matters. Too low and you serve near-misses that aren't really the same question (false hits). Too high and you behave like an exact-match cache and rarely hit. It's tunable per pattern and per workspace — an FAQ bot can be looser than a code assistant.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pii-safe-by-default">PII-safe by default<a href="https://dvarahq.com/blog/semantic-caching-llms#pii-safe-by-default" class="hash-link" aria-label="Direct link to PII-safe by default" title="Direct link to PII-safe by default" translate="no">​</a></h2>
<p>Here's the governance failure mode a naive cache walks into: it becomes a store of raw sensitive data. If prompts or responses can contain PII, and you cache them as-is, your cache is now an unaudited copy of the very data your policies exist to protect.</p>
<p>In a governed gateway, PII is <strong>stripped before anything is written to the cache</strong> — the redaction that runs on the request path also runs before the cache write, so the cache never holds raw sensitive data. Caching is a performance feature that must not become a compliance hole; closing that gap by default is the difference between a cache and a governed cache.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-a-cache-hit-must-cost-0">Why a cache hit must cost $0<a href="https://dvarahq.com/blog/semantic-caching-llms#why-a-cache-hit-must-cost-0" class="hash-link" aria-label="Direct link to Why a cache hit must cost $0" title="Direct link to Why a cache hit must cost $0" translate="no">​</a></h2>
<p>The other quiet failure: billing. When you serve a response from cache, <strong>no upstream API call happened</strong> — so that request should cost you nothing. A cache that still books the original call's cost against your budget on every hit erases the savings the cache exists to create.</p>
<p>Done right, a cache hit is <em>recorded</em> (you want the token volume for reporting) but <em>billed at $0</em>, because there was no upstream spend. That distinction — served volume vs. actual upstream cost — is what makes caching a real <strong><a class="" href="https://dvarahq.com/blog/llm-cost-management">cost governance</a></strong> lever and not just a latency trick. On a workload with repetitive prompts, it's one of the largest cost reductions available.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-latency-win">The latency win<a href="https://dvarahq.com/blog/semantic-caching-llms#the-latency-win" class="hash-link" aria-label="Direct link to The latency win" title="Direct link to The latency win" translate="no">​</a></h2>
<p>Cost is the headline, but hits are also <em>fast</em>. A cached response skips the round trip to the provider entirely — sub-hundred-millisecond instead of multi-second. For high-repetition surfaces (support bots, autocomplete, templated queries), that's a materially better user experience on top of the savings.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="trade-offs-to-respect">Trade-offs to respect<a href="https://dvarahq.com/blog/semantic-caching-llms#trade-offs-to-respect" class="hash-link" aria-label="Direct link to Trade-offs to respect" title="Direct link to Trade-offs to respect" translate="no">​</a></h2>
<ul>
<li class=""><strong>Staleness.</strong> A cached answer can go out of date. TTLs and per-pattern scoping keep it fresh.</li>
<li class=""><strong>False hits.</strong> Two prompts can be <em>close</em> without being the <em>same</em>. The similarity threshold, tuned per use case, is your control.</li>
<li class=""><strong>Personalization and correctness.</strong> Don't cache responses that are user-specific or must be freshly computed. Scope caching to surfaces where a shared answer is genuinely correct.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="an-evaluation-checklist">An evaluation checklist<a href="https://dvarahq.com/blog/semantic-caching-llms#an-evaluation-checklist" class="hash-link" aria-label="Direct link to An evaluation checklist" title="Direct link to An evaluation checklist" translate="no">​</a></h2>
<ul>
<li class="">Does it match on <strong>meaning</strong> (embeddings + similarity), or only exact text?</li>
<li class="">Is the <strong>similarity threshold</strong> tunable per pattern/workspace?</li>
<li class="">Is <strong>PII stripped before caching</strong>, so the cache isn't a sensitive-data store?</li>
<li class="">Does a cache hit book <strong>$0</strong> cost, or does it re-charge the original call?</li>
<li class="">Are there <strong>TTLs</strong> and per-pattern scoping to control staleness?</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/semantic-caching-llms#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p><strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA</a></strong> does per-workspace caching inside its governed path, with TTLs, PII stripped before anything is cached, and a cache hit that records served token volume but books <strong>$0</strong> cost — no upstream call, no charge. Note what is <strong>off by default</strong>: DVARA uses vectors as a prefilter but serves only on an exact cache-key match unless you set <code>similarity-serving: true</code>. That is a measured decision rather than caution — over a 20-pair corpus the three highest-scoring pairs were all ones that must <em>not</em> be served (two negations and a word-order swap), and precision never exceeded 0.38 at any threshold. Turn it on where a topically-similar answer is acceptable; leave it off where a wrong answer is a correctness problem. It's a cost-and-latency win that stays a governance-safe surface. See how it ties into spend in the <strong><a class="" href="https://dvarahq.com/blog/llm-cost-management">LLM cost management</a></strong> deep dive, or the pillar, <strong><a class="" href="https://dvarahq.com/llm-gateway">What Is an LLM Gateway?</a></strong>.</p>]]></content:encoded>
            <category>governance</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[Self-Hosted LLM Gateway: Governance That Runs Inside Your Perimeter]]></title>
            <link>https://dvarahq.com/blog/self-hosted-llm-gateway</link>
            <guid>https://dvarahq.com/blog/self-hosted-llm-gateway</guid>
            <pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[For regulated data, where governance runs matters as much as what it does. Keep prompts, policy enforcement and audit inside your own perimeter, with BYOK.]]></description>
            <content:encoded><![CDATA[<p>For many teams, the deciding question is not only what an AI gateway does, but where it runs. If prompts contain patient records, financial data, or source code, adding another vendor to the request path can complicate—or prevent—approval.</p>
<p>Self-hosting keeps the enforcement point inside your environment. Policy evaluation and PII handling happen before a request reaches a model provider, and a denied request never reaches one.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-self-hosted-means-here">What "self-hosted" means here<a href="https://dvarahq.com/blog/self-hosted-llm-gateway#what-self-hosted-means-here" class="hash-link" aria-label="Direct link to What &quot;self-hosted&quot; means here" title="Direct link to What &quot;self-hosted&quot; means here" translate="no">​</a></h2>
<p>A self-hosted deployment is one you run inside your own environment — your VPC, your Kubernetes cluster, your on-prem data center — rather than consuming it as a vendor-operated SaaS. Requests from your applications hit <em>your</em> instance. The Gateway still calls out to model providers (unless you're running fully local models), but it does so from inside your perimeter, under your network controls, with your keys, after your governance has run.</p>
<p><strong>Self-hosted and Open Source describe different choices.</strong> DVARA Open Source is Apache-2.0 and runs as a file-configured LLM Gateway without a database. The Enterprise platform is also self-hosted, but adds Flightdeck, central persistence, fleet operations, advanced LLM controls, and the MCP and A2A planes. In either case, the deployment and request path stay in your environment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-self-hosting-actually-buys-you">What self-hosting actually buys you<a href="https://dvarahq.com/blog/self-hosted-llm-gateway#what-self-hosting-actually-buys-you" class="hash-link" aria-label="Direct link to What self-hosting actually buys you" title="Direct link to What self-hosting actually buys you" translate="no">​</a></h2>
<p>Ordered governance-first, because that's the reason to do it:</p>
<ul>
<li class=""><strong>Governance runs before the provider sees the prompt.</strong> The most sensitive thing you send a model is often the prompt itself. Self-hosting means the request is policy-checked, PII-scanned, and logged <em>before</em> it reaches a model provider — and a blocked request never reaches one at all.</li>
<li class=""><strong>Audit stays in your control.</strong> Open Source can write a local tamper-evident audit file. Enterprise stores fleet evidence in your database for search, export, and reporting.</li>
<li class=""><strong>Data residency you control.</strong> You decide which region the platform runs in and which providers it may reach — how you keep EU traffic on EU infrastructure. (This is the bridge to the broader <strong><a class="" href="https://dvarahq.com/ai-audit-compliance">AI compliance and sovereignty</a></strong> story.)</li>
<li class=""><strong>Your keys, your vault.</strong> Enterprise provider credentials can resolve from your secret store rather than a vendor-operated service.</li>
<li class=""><strong>Air-gap-friendly.</strong> Paired with local models, it can run with no outbound internet at all.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="byok-bring-your-own-keys">BYOK: bring your own keys<a href="https://dvarahq.com/blog/self-hosted-llm-gateway#byok-bring-your-own-keys" class="hash-link" aria-label="Direct link to BYOK: bring your own keys" title="Direct link to BYOK: bring your own keys" translate="no">​</a></h2>
<p>A serious self-hosted platform is <strong>BYOK</strong> — bring your own keys. Each workspace's provider credentials are theirs; the Gateway resolves the right key per request and never forces everyone through one shared secret. Credentials can be stored encrypted at rest, or — better — as <em>references</em> into a vault (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault), so the secret material never lands in the platform's own database at all. Rotate a key, and in-flight resolution picks up the new one without a redeploy.</p>
<p>For the strictest setups, <strong>strict BYOK</strong> refuses to let a workspace borrow a shared platform key at all — no own credential, no call — so you can guarantee cross-workspace key isolation as a governed policy.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-self-hosting-doesnt-do-for-free">What self-hosting doesn't do for free<a href="https://dvarahq.com/blog/self-hosted-llm-gateway#what-self-hosting-doesnt-do-for-free" class="hash-link" aria-label="Direct link to What self-hosting doesn't do for free" title="Direct link to What self-hosting doesn't do for free" translate="no">​</a></h2>
<p>Self-hosting is a governance-and-data-path guarantee, not a magic wand:</p>
<ul>
<li class=""><strong>You operate it.</strong> Open Source can run as one process with no database. Enterprise adds PostgreSQL and the services needed for central operations.</li>
<li class=""><strong>You still call external providers</strong> unless you run local models; self-hosting the platform doesn't self-host OpenAI.</li>
<li class=""><strong>You still own the operating work.</strong> Self-hosting trades a vendor-managed request path for your own infrastructure, upgrades, and monitoring.</li>
</ul>
<p>For most regulated buyers, those are easy trades against the alternative of <em>not being allowed to use the product at all</em>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="an-evaluation-checklist">An evaluation checklist<a href="https://dvarahq.com/blog/self-hosted-llm-gateway#an-evaluation-checklist" class="hash-link" aria-label="Direct link to An evaluation checklist" title="Direct link to An evaluation checklist" translate="no">​</a></h2>
<ul>
<li class="">Does it deploy into <strong>your VPC / cluster</strong> cleanly (containers, a Helm chart)?</li>
<li class="">Does <strong>governance run before the provider sees the request</strong> — policy and PII enforced on every call?</li>
<li class="">Does the <strong>audit trail stay in your database</strong>, outside any vendor's scope?</li>
<li class="">Is it <strong>BYOK</strong>, with per-workspace credential isolation (and a strict mode)?</li>
<li class="">Can credentials live in your <strong>vault</strong> as references, not just encrypted in its DB?</li>
<li class="">Can you pin it to a <strong>region</strong> and restrict which providers it may reach?</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/self-hosted-llm-gateway#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p><strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA</a></strong> is an AI gateway that governs model, tool and agent-to-agent calls and runs in your environment. Start with the file-configured Open Source LLM Gateway, or use Enterprise when you need Flightdeck, central evidence, fleet operations, per-workspace credentials, and vault-backed secrets. For the wider regulatory picture, read the <strong><a class="" href="https://dvarahq.com/ai-audit-compliance">AI Audit &amp; Compliance guide</a></strong>.</p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
        </item>
        <item>
            <title><![CDATA[Control LLM Costs Before the Invoice]]></title>
            <link>https://dvarahq.com/blog/llm-cost-management</link>
            <guid>https://dvarahq.com/blog/llm-cost-management</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[AI cost governance attributes and enforces spend at the control point, not after the invoice: real-time attribution, hard budget caps, and chargeback.]]></description>
            <content:encoded><![CDATA[<p>The first LLM invoice that makes someone's stomach drop is a rite of passage. Spend is driven by token counts nobody was watching, spread across teams nobody was attributing, on models nobody chose deliberately. By the time it shows up on a bill, the money is gone and the cause is a forensic exercise.</p>
<p>Most tools answer this with a dashboard — visibility after the fact. That's the difference between observation and <strong>governance</strong>: a dashboard tells you what you spent; a governed control point can <em>stop</em> you from spending it. Real cost governance moves control upstream, to the moment each call is made — <a class="" href="https://dvarahq.com/docs/cookbook/per-workspace-cost-attribution">attributing it</a>, and <a class="" href="https://dvarahq.com/docs/rate-limiting"><em>enforcing</em> a budget</a> on it, on every call. That enforcement lives in the <strong>DVARA LLM Gateway</strong>, the part of DVARA that handles model calls.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-llm-cost-is-hard-to-control">Why LLM cost is hard to control<a href="https://dvarahq.com/blog/llm-cost-management#why-llm-cost-is-hard-to-control" class="hash-link" aria-label="Direct link to Why LLM cost is hard to control" title="Direct link to Why LLM cost is hard to control" translate="no">​</a></h2>
<p>Three properties make model spend uniquely slippery:</p>
<ol>
<li class=""><strong>It's usage-priced and unpredictable.</strong> Cost scales with tokens in and out, and a single verbose prompt or a runaway agent loop can cost more than a thousand ordinary calls.</li>
<li class=""><strong>It's invisible until it's an invoice.</strong> Without instrumentation on the request path, you find out what you spent after you've spent it.</li>
<li class=""><strong>It's un-attributed.</strong> "Which team, which feature, which customer drove this?" has no answer if every call looks the same.</li>
</ol>
<p>A governed control point sits on the one path every call takes, which makes it the only place you can price, attribute, and <em>cap</em> spend as it happens.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-time-cost-attribution">Real-time cost attribution<a href="https://dvarahq.com/blog/llm-cost-management#real-time-cost-attribution" class="hash-link" aria-label="Direct link to Real-time cost attribution" title="Direct link to Real-time cost attribution" translate="no">​</a></h2>
<p>Every request through the Gateway is priced the moment it completes — token counts × the per-model rate — and written as a cost record tagged with the workspace, API key, model, and provider. That turns the unanswerable question into a query: cost by team, by model, by customer, by arbitrary tag, over any window. Attribution is the accountability half of cost governance.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="budget-caps-enforcement-with-teeth">Budget caps: enforcement with teeth<a href="https://dvarahq.com/blog/llm-cost-management#budget-caps-enforcement-with-teeth" class="hash-link" aria-label="Direct link to Budget caps: enforcement with teeth" title="Direct link to Budget caps: enforcement with teeth" translate="no">​</a></h2>
<p>Attribution tells you what happened; <strong>budgets</strong> control what's allowed to happen — the enforcement half. A budget cap sets a spend ceiling for a scope, and it has two teeth:</p>
<ul>
<li class=""><strong>Soft limit</strong> — crossing it fires a warning (an alert, a webhook) but lets traffic continue. Your early warning.</li>
<li class=""><strong>Hard limit</strong> — crossing it <em>blocks</em> further spend with a clear "budget exceeded" response, so a runaway process can't drain the month in an afternoon.</li>
</ul>
<p>Budgets nest — a global cap, a per-workspace cap, a per-key cap — and the <strong>tightest applicable limit wins</strong>, so platform-wide guardrails and team-level budgets coexist without stepping on each other. Every block is an audited governance event, not a silent drop.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="model-downgrade-under-pressure">Model downgrade under pressure<a href="https://dvarahq.com/blog/llm-cost-management#model-downgrade-under-pressure" class="hash-link" aria-label="Direct link to Model downgrade under pressure" title="Direct link to Model downgrade under pressure" translate="no">​</a></h2>
<p>Blocking isn't the only lever. When a workspace approaches its budget, the Gateway can <strong>automatically downgrade</strong> the model — routing <code>gpt-4o</code> to <code>gpt-4o-mini</code>, say — so the workload keeps running at a fraction of the cost instead of stopping outright. A softer governed response: degrade the expensive path before you close it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="per-call-ceilings">Per-call ceilings<a href="https://dvarahq.com/blog/llm-cost-management#per-call-ceilings" class="hash-link" aria-label="Direct link to Per-call ceilings" title="Direct link to Per-call ceilings" translate="no">​</a></h2>
<p>Accumulated-spend budgets don't catch a <em>single</em> pathological call — one request with a huge <code>max_tokens</code> that alone costs more than it should. An optional <strong>per-call cost ceiling</strong> estimates a request's worst-case cost before dispatch and rejects the outliers, complementing (not replacing) the running budget.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="caching-and-routing-are-cost-levers-too">Caching and routing are cost levers too<a href="https://dvarahq.com/blog/llm-cost-management#caching-and-routing-are-cost-levers-too" class="hash-link" aria-label="Direct link to Caching and routing are cost levers too" title="Direct link to Caching and routing are cost levers too" translate="no">​</a></h2>
<p>The cheapest call is the one you do not make. Serving a repeat prompt from cache costs nothing upstream because no provider call happened. In DVARA, exact cache-key matching is the default; matching a merely <em>similar</em> prompt is opt-in. That makes <strong><a class="" href="https://dvarahq.com/blog/semantic-caching-llms">semantic caching</a></strong> a direct cost-control tool. Routing matters too: sending suitable work to a cheaper model is a <strong><a class="" href="https://dvarahq.com/blog/multi-provider-llm-routing">routing</a></strong> decision with a measurable effect on spend.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="chargeback-and-forecasting">Chargeback and forecasting<a href="https://dvarahq.com/blog/llm-cost-management#chargeback-and-forecasting" class="hash-link" aria-label="Direct link to Chargeback and forecasting" title="Direct link to Chargeback and forecasting" translate="no">​</a></h2>
<p>For finance, raw cost records aren't enough — they need <strong>chargeback</strong>: per-workspace reports (PDF or CSV) that turn platform spend into numbers a business unit can be billed for. Add <strong>forecasting</strong> (project the month from the trailing run rate) and <strong>anomaly detection</strong> (flag a workspace spending 2× its baseline), and cost management becomes proactive governance instead of a monthly autopsy.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="an-evaluation-checklist">An evaluation checklist<a href="https://dvarahq.com/blog/llm-cost-management#an-evaluation-checklist" class="hash-link" aria-label="Direct link to An evaluation checklist" title="Direct link to An evaluation checklist" translate="no">​</a></h2>
<ul>
<li class="">Is cost <strong>attributed in real time</strong>, per workspace/key/model — or only in aggregate, after the fact?</li>
<li class="">Are there <strong>hard budget caps that block</strong>, or only dashboards that inform?</li>
<li class="">Does it <strong>downgrade</strong> under budget pressure, or only stop?</li>
<li class="">Can it catch a <strong>single runaway call</strong>, not just accumulated spend?</li>
<li class="">Does it produce <strong>chargeback</strong> reports and <strong>forecasts</strong> finance can use?</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/llm-cost-management#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p><strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA</a></strong> is an AI gateway that governs model, tool and agent-to-agent calls: its LLM Gateway prices every call in real time and attributes it by workspace, key, model, provider, and custom tag; enforces nested soft/hard budget caps (tightest wins); auto-downgrades models under budget pressure; catches runaway single calls with an optional per-call ceiling; and generates chargeback reports, forecasts, and anomaly alerts. Cache hits book $0. Cost isn't a dashboard bolted on afterward — it's enforced on the governed path. Read the pillar guide, <strong><a class="" href="https://dvarahq.com/llm-gateway">What Is an LLM Gateway?</a></strong>, for how cost governance fits the rest.</p>]]></content:encoded>
            <category>governance</category>
            <category>finops</category>
        </item>
        <item>
            <title><![CDATA[DVARA 1.1.0 — Native MCP, SSO Presets, and Turnkey Kubernetes]]></title>
            <link>https://dvarahq.com/blog/dvara-1-1-0-ga</link>
            <guid>https://dvarahq.com/blog/dvara-1-1-0-ga</guid>
            <pubDate>Sun, 28 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[DVARA 1.1.0 is generally available — native MCP protocol support, Keycloak and Auth0 SSO presets, and verified GKE and DOKS Kubernetes deployment.]]></description>
            <content:encoded><![CDATA[<p>DVARA 1.1.0 is generally available. It makes three parts of a production deployment easier to use: <strong>native MCP protocol support</strong>, verified <strong>Keycloak and Auth0 SSO presets</strong>, and a <strong>Kubernetes deployment</strong> tested end to end on GKE and DOKS.</p>
<p>Everything below ships in every Enterprise install — the same governance core, the same MCP Proxy, the same agentic controls. 1.1.0 is a drop-in upgrade from 1.0.x.</p>
<!-- -->
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>Updated 13 September 2026</div><div class="admonitionContent_BuS1"><p>This post describes 1.1.0 as it shipped. DVARA 1.8 has separate Open Source and
Enterprise distributions. Enterprise LLM governance and SSO run without a key
in Development posture; a licence grants production rights and support and
activates the MCP and A2A planes. The component called <strong>MCP Proxy</strong> here is now
the <strong>DVARA MCP Gateway</strong>.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="native-mcp--speak-the-protocol-keep-the-governance">Native MCP — speak the protocol, keep the governance<a href="https://dvarahq.com/blog/dvara-1-1-0-ga#native-mcp--speak-the-protocol-keep-the-governance" class="hash-link" aria-label="Direct link to Native MCP — speak the protocol, keep the governance" title="Direct link to Native MCP — speak the protocol, keep the governance" translate="no">​</a></h2>
<p>In 1.0.0 the MCP Proxy governed tool calls over a REST surface. In 1.1.0 the gateway speaks <strong>native MCP</strong> (protocol revision <code>2025-06-18</code>) on both sides, behind the same transport-agnostic governance filter chain.</p>
<p>Northbound, DVARA exposes a native <code>/mcp</code> Streamable-HTTP endpoint. Point Claude Desktop, Cursor, or any MCP-compatible agent straight at your gateway and it gets a per-workspace <code>tools/list</code> — namespaced as <code>{serverId}__{tool}</code>, scanned for tool poisoning — and a <code>tools/call</code> that runs through the full chain: policy evaluation, argument-level tool policies, human approval gates enforced at execution, PII scanning, and signed, chained audit. The client sees a standard MCP server; you get governance and a complete audit trail transparently, on call one.</p>
<p>A note on transports. The MCP spec defines two standard transports — <strong>stdio</strong> and <strong>Streamable HTTP</strong>, the latter having superseded the older HTTP+SSE transport. Northbound, DVARA's <code>/mcp</code> endpoint is Streamable HTTP by design — the one network-native transport that carries per-workspace auth and scales across pods; stdio- or SSE-only clients reach it through a local bridge such as <code>mcp-remote</code>. Southbound, DVARA reaches upstream MCP servers through a pooled native client, dispatched per server on its configured transport: <strong>Streamable HTTP</strong>, <strong>stdio</strong>, and the <strong>legacy HTTP+SSE</strong> transport for older servers — while DVARA's existing REST integration remains the default and is unchanged. Native is opt-in per upstream.</p>
<p>The agent governance you already had — loop detection with auto-kill, the unified LLM + MCP session timeline, approval queues — now extends across native MCP traffic without a protocol translation layer in the middle. See <a class="" href="https://dvarahq.com/docs/native-mcp">Native MCP Support</a> in the docs for the full protocol surface — transports, namespacing, and the documented limits.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="drop-in-sso--keycloak-and-auth0-presets">Drop-in SSO — Keycloak and Auth0 presets<a href="https://dvarahq.com/blog/dvara-1-1-0-ga#drop-in-sso--keycloak-and-auth0-presets" class="hash-link" aria-label="Direct link to Drop-in SSO — Keycloak and Auth0 presets" title="Direct link to Drop-in SSO — Keycloak and Auth0 presets" translate="no">​</a></h2>
<p>Wiring an external identity provider into the DVARA Console used to mean hand-mapping OIDC claims. 1.1.0 ships <strong>verified configuration presets</strong> for Keycloak and Auth0 — tested against real IdPs, with step-by-step setup guides. Activate a preset with a Spring profile, point it at your realm or workspace, and SSO to the Console works, including Auth0's namespaced claim URIs out of the box. SAML 2.0 and generic OIDC remain supported for everything else.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="turnkey-kubernetes--verified-gke-and-doks">Turnkey Kubernetes — verified GKE and DOKS<a href="https://dvarahq.com/blog/dvara-1-1-0-ga#turnkey-kubernetes--verified-gke-and-doks" class="hash-link" aria-label="Direct link to Turnkey Kubernetes — verified GKE and DOKS" title="Direct link to Turnkey Kubernetes — verified GKE and DOKS" translate="no">​</a></h2>
<p>Running DVARA on managed Kubernetes is now a documented, validated path rather than an exercise. We brought the stack up end-to-end on two clouds and wrote the recipes down:</p>
<ul>
<li class=""><strong>GKE</strong> — Autopilot or Standard, Cloud SQL over private IP, Workload Identity for secrets, GCE Ingress with managed TLS.</li>
<li class=""><strong>DigitalOcean Kubernetes (DOKS)</strong> — DO Managed PostgreSQL, a plain Kubernetes Secret, Traefik v3 ingress with Let's Encrypt.</li>
</ul>
<p>The Helm chart now injects the signed license into every component from a single platform Secret, so all three services boot from one source of truth. Per-cloud guides are in the docs, with reference recipes you can adapt to your own cluster.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="usage-visibility">Usage visibility<a href="https://dvarahq.com/blog/dvara-1-1-0-ga#usage-visibility" class="hash-link" aria-label="Direct link to Usage visibility" title="Direct link to Usage visibility" translate="no">​</a></h2>
<p>Every token through the LLM gateway and every MCP call is metered per workspace, with a monthly usage report you can export — for cost attribution, chargeback, and capacity planning. Universal on every install, as it has been since 1.0.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="upgrade">Upgrade<a href="https://dvarahq.com/blog/dvara-1-1-0-ga#upgrade" class="hash-link" aria-label="Direct link to Upgrade" title="Direct link to Upgrade" translate="no">​</a></h2>
<p>1.1.0 is a drop-in upgrade. Pull the <code>1.1.0</code> images (or the <code>oci://ghcr.io/dvarahq/charts/dvara:1.1.0</code> chart), keep your existing license and configuration, and roll. The Spring Boot 4.1.0 platform bump and a round of base-image and dependency CVE fixes come along for free.</p>
<p>If you are new to DVARA: it is self-managed, activated by a signed license key, with bring-your-own-key credentials so provider keys and request data never leave your perimeter. Start with a <strong>30-day, all-features trial license</strong> — every feature unlocked, including native MCP and agentic governance, no credit card. <a class="" href="https://dvarahq.com/pricing">Get the trial</a> or, for production licensing or a managed deployment in your region, <a class="" href="https://dvarahq.com/pricing">talk to us</a>. The release is live now.</p>]]></content:encoded>
            <category>governance</category>
            <category>mcp</category>
        </item>
        <item>
            <title><![CDATA[How LLM Fallback and Failover Work]]></title>
            <link>https://dvarahq.com/blog/llm-fallback-failover</link>
            <guid>https://dvarahq.com/blog/llm-fallback-failover</guid>
            <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Reliability is a governance requirement, not only an SRE one. Circuit breakers and capability-aware failover keep AI traffic serving, and audited, in a failure.]]></description>
            <content:encoded><![CDATA[<p>Model providers are remarkably good and occasionally unavailable. They rate-limit you at the worst moment, degrade under load, and have real outages. If your application talks to a provider directly, every one of those events is a user-facing failure — and every team ends up writing its own retry loop, usually badly.</p>
<p>Resilience belongs in the same layer as governance, and for the same reason: it can't be trusted if it's scattered and invisible. When failover runs at a single governed control point — the <strong>DVARA LLM Gateway</strong>, the part of DVARA that handles model calls — every retry and provider switch is policy-checked and written to the audit trail, so "we failed over to a different provider" is a recorded, reviewable event, not a silent mystery. <strong>LLM fallback and failover</strong> move resilience out of application code and onto the governed path.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="fallback-vs-failover">Fallback vs. failover<a href="https://dvarahq.com/blog/llm-fallback-failover#fallback-vs-failover" class="hash-link" aria-label="Direct link to Fallback vs. failover" title="Direct link to Fallback vs. failover" translate="no">​</a></h2>
<p>The terms get used interchangeably; the distinction is useful:</p>
<ul>
<li class=""><strong>Failover</strong> — the <em>primary</em> provider fails (an error, a 429, a timeout, an open circuit) and the request is retried against an <em>alternative</em> that can serve it.</li>
<li class=""><strong>Fallback</strong> — the ordered list of alternatives to try. Your fallback chain is the plan; failover is executing it.</li>
</ul>
<p>Both live naturally at a governed control point, because it's the one place that already knows every provider, their health, and their capabilities — and governs the switch.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-circuit-breaker">The circuit breaker<a href="https://dvarahq.com/blog/llm-fallback-failover#the-circuit-breaker" class="hash-link" aria-label="Direct link to The circuit breaker" title="Direct link to The circuit breaker" translate="no">​</a></h2>
<p>Retrying a provider that's clearly down just adds latency and load. A <strong>circuit breaker</strong> tracks failures per provider and, once they cross a threshold, "opens" — the Gateway stops sending traffic there for a cooldown window and fails straight over to an alternative. When the cooldown passes, it lets a probe through; if that succeeds, the circuit closes and normal traffic resumes.</p>
<p>This protects both sides: your users don't wait on a doomed call, and you don't hammer a struggling provider while it's trying to recover.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="capability-aware-failover--the-part-thats-easy-to-get-wrong">Capability-aware failover — the part that's easy to get wrong<a href="https://dvarahq.com/blog/llm-fallback-failover#capability-aware-failover--the-part-thats-easy-to-get-wrong" class="hash-link" aria-label="Direct link to Capability-aware failover — the part that's easy to get wrong" title="Direct link to Capability-aware failover — the part that's easy to get wrong" translate="no">​</a></h2>
<p>Here's the failure mode naive failover walks into. A request asks for <code>response_format: json_schema</code>. The primary — which supports structured outputs — is down. A naive gateway fails over to the next provider in the list… which doesn't support structured outputs. Now the request fails <em>anyway</em>, with a confusing error, and your "resilience" made things worse.</p>
<p><strong>Capability-aware failover</strong> filters the fallback candidates by what the request actually needs <em>before</em> retrying. If the request needs structured output, only structured-output-capable providers are considered. If no capable fallback exists, the Gateway says so explicitly — a clear "no capable provider for this request" error — instead of failing over into a wall. This is the same capability filtering that governs normal <strong><a class="" href="https://dvarahq.com/blog/multi-provider-llm-routing">multi-provider routing</a></strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="upstream-rate-limits-shed-before-you-fail">Upstream rate limits: shed before you fail<a href="https://dvarahq.com/blog/llm-fallback-failover#upstream-rate-limits-shed-before-you-fail" class="hash-link" aria-label="Direct link to Upstream rate limits: shed before you fail" title="Direct link to Upstream rate limits: shed before you fail" translate="no">​</a></h2>
<p>The best failover is the one you never trigger. Providers publish rate-limit headers on every response — how much of your token and request budget remains. A control point that reads those can <em>proactively</em> shed or shift traffic as you approach a limit, rather than waiting for the 429. Under pressure it can honor workspace priority — protecting premium traffic and letting bulk traffic absorb the throttling first. You degrade deliberately instead of hitting a wall, and the shed is attributed and audited like any other governed decision.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="resilience-you-can-prove">Resilience you can prove<a href="https://dvarahq.com/blog/llm-fallback-failover#resilience-you-can-prove" class="hash-link" aria-label="Direct link to Resilience you can prove" title="Direct link to Resilience you can prove" translate="no">​</a></h2>
<p>Failover only earns trust if you can observe <em>and</em> account for it. Which providers are failing over, how often, to whom? What's the retry rate, the circuit-open rate, the fallback rate by provider pair? Those are Prometheus metrics and audit events, not guesswork — which is why resilience and <strong><a class="" href="https://dvarahq.com/blog/llm-observability">observability</a></strong> are two sides of the same coin. A governed gateway doesn't just recover from a provider outage; it can show you exactly what it did.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-checklist-for-evaluating-failover">A checklist for evaluating failover<a href="https://dvarahq.com/blog/llm-fallback-failover#a-checklist-for-evaluating-failover" class="hash-link" aria-label="Direct link to A checklist for evaluating failover" title="Direct link to A checklist for evaluating failover" translate="no">​</a></h2>
<ul>
<li class="">Does it use a <strong>circuit breaker</strong>, or just blind retries?</li>
<li class="">Is failover <strong>capability-aware</strong>, or will it retry on a model that can't serve the request?</li>
<li class="">Does it react to <strong>upstream rate-limit headers</strong> before the 429, or only after?</li>
<li class="">Does it honor <strong>workspace priority</strong> when a shared credential is near its limit?</li>
<li class="">Are failover events <strong>observable and audited</strong> — metrics and a trail per provider pair, not just a log line?</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-dvara-fits">Where DVARA fits<a href="https://dvarahq.com/blog/llm-fallback-failover#where-dvara-fits" class="hash-link" aria-label="Direct link to Where DVARA fits" title="Direct link to Where DVARA fits" translate="no">​</a></h2>
<p><strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA</a></strong> is an AI gateway that governs model, tool and agent-to-agent calls. Its LLM Gateway wraps each provider in a circuit breaker, fails over capability-aware (a request that needs structured output only retries on a provider that supports it, with a clear error if none can), and reads upstream rate-limit headers to shed or shift traffic — honoring workspace priority — before a 429 fires. Every retry, fallback, and shed is a governed, audited, observable event. Read the pillar guide, <strong><a class="" href="https://dvarahq.com/llm-gateway">What Is an LLM Gateway?</a></strong>, for how resilience fits with routing and cost control.</p>]]></content:encoded>
            <category>governance</category>
            <category>architecture</category>
        </item>
        <item>
            <title><![CDATA[Multi-Provider LLM Routing in an LLM Gateway]]></title>
            <link>https://dvarahq.com/blog/multi-provider-llm-routing</link>
            <guid>https://dvarahq.com/blog/multi-provider-llm-routing</guid>
            <pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How multi-provider LLM routing works in an LLM gateway: when to use round-robin, weighted, canary, latency or cost routing, and how it differs from failover.]]></description>
            <content:encoded><![CDATA[<p><strong>Multi-provider LLM routing</strong> sends each model request to an eligible provider according to a declared strategy while keeping policy, PII controls, rate limits, audit, and cost attribution on the same request path. It solves a platform problem: applications should not each implement their own provider selection and silently drift into different governance behavior.</p>
<p>DVARA is an AI gateway that governs model, tool and agent-to-agent calls; the <strong>DVARA LLM Gateway</strong> is the component that routes model traffic. The application keeps one OpenAI-compatible endpoint, while the Gateway chooses a provider only after governance checks and capability filtering.</p>
<!-- -->
<p><em>Updated September 24, 2026 for DVARA 1.8.0.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-multi-provider-llm-routing">What is multi-provider LLM routing?<a href="https://dvarahq.com/blog/multi-provider-llm-routing#what-is-multi-provider-llm-routing" class="hash-link" aria-label="Direct link to What is multi-provider LLM routing?" title="Direct link to What is multi-provider LLM routing?" translate="no">​</a></h2>
<p>Multi-provider LLM routing is the controlled selection of an upstream model provider for each request. A route matches the requested model, removes providers that cannot satisfy the request, then applies a strategy such as round-robin or weighted distribution to the remaining pool.</p>
<p>That definition has three important boundaries:</p>
<ul>
<li class=""><strong>Routing is not model rewriting.</strong> The selected provider receives the model identifier from the request. Every provider in a shared route must accept that identifier.</li>
<li class=""><strong>Routing is not failover.</strong> Routing selects the primary provider before dispatch; failover is a recovery step after an eligible provider failure.</li>
<li class=""><strong>Routing is not governance by itself.</strong> Provider selection still needs policy enforcement, PII handling, rate limits, and an evidence trail around it.</li>
</ul>
<p>Centralizing the decision gives platform teams one place to review routing behavior without moving the decision into every application repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-does-an-llm-routing-decision-work">How does an LLM routing decision work?<a href="https://dvarahq.com/blog/multi-provider-llm-routing#how-does-an-llm-routing-decision-work" class="hash-link" aria-label="Direct link to How does an LLM routing decision work?" title="Direct link to How does an LLM routing decision work?" translate="no">​</a></h2>
<p>A governed request follows five distinct stages:</p>
<ol>
<li class=""><strong>Evaluate governance.</strong> Resolve the API key and workspace, then apply policy, guardrails, PII handling, and configured limits.</li>
<li class=""><strong>Match a route.</strong> Compare the request's <code>model</code> with configured route patterns. If no route matches, use the built-in model-prefix mapping.</li>
<li class=""><strong>Filter by capability.</strong> Remove providers that cannot satisfy requirements such as structured output, JSON mode, or tool calling.</li>
<li class=""><strong>Apply the routing strategy.</strong> Select one provider from the eligible pool.</li>
<li class=""><strong>Recover separately.</strong> If the upstream call fails and resilience permits recovery, try a compatible registered fallback.</li>
</ol>
<p>This order prevents a routing decision from bypassing governance. It also prevents failover from sending a structured-output or tool-calling request to a provider that cannot honor it. If no compatible provider exists, the Gateway returns an explicit capability-mismatch error instead of silently degrading the request.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="which-routing-strategy-should-you-use">Which routing strategy should you use?<a href="https://dvarahq.com/blog/multi-provider-llm-routing#which-routing-strategy-should-you-use" class="hash-link" aria-label="Direct link to Which routing strategy should you use?" title="Direct link to Which routing strategy should you use?" translate="no">​</a></h2>
<p>Choose the simplest strategy that expresses the operational decision you need to make.</p>
<table><thead><tr><th>Strategy</th><th>Use it when</th><th>Availability</th></tr></thead><tbody><tr><td>Model prefix</td><td>A model family has one natural provider, such as <code>gpt-*</code> to OpenAI or <code>claude-*</code> to Anthropic</td><td>Open Source and Enterprise</td></tr><tr><td>Round-robin</td><td>Registered providers are interchangeable and should receive an even share</td><td>Open Source and Enterprise</td></tr><tr><td>Weighted</td><td>You need a controlled traffic split, such as 80/20, between compatible providers</td><td>Open Source and Enterprise</td></tr><tr><td>Canary</td><td>You want to bound exposure to a candidate provider while keeping a baseline</td><td>Open Source and Enterprise</td></tr><tr><td>Latency-aware</td><td>Selection should use observed provider latency</td><td>Enterprise</td></tr><tr><td>Cost-aware</td><td>Selection should consider configured model cost and an optional latency constraint</td><td>Enterprise</td></tr><tr><td>Geo-aware</td><td>Provider region must participate in selection</td><td>Enterprise</td></tr><tr><td>Intelligent</td><td>Request complexity should select a configured model tier</td><td>Enterprise</td></tr></tbody></table>
<p>DVARA Open Source 1.8.0 does <strong>not</strong> provide latency-aware, cost-aware, geo-aware, or intelligent routing. Configuring one of those strategies in the Open Source build is refused at startup rather than silently falling back to another strategy.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configure-weighted-routing-without-changing-application-code">Configure weighted routing without changing application code<a href="https://dvarahq.com/blog/multi-provider-llm-routing#configure-weighted-routing-without-changing-application-code" class="hash-link" aria-label="Direct link to Configure weighted routing without changing application code" title="Direct link to Configure weighted routing without changing application code" translate="no">​</a></h2>
<p>This <code>gateway.yaml</code> registers OpenAI and Azure OpenAI, then sends roughly 80% of matching traffic to OpenAI and 20% to Azure OpenAI:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">providers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> openai</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">api_key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">type</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> azure</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">openai</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">api_key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">AZURE_OPENAI_API_KEY</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">base_url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">AZURE_OPENAI_BASE_URL</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">routes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> weighted</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">gpt</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">model</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"gpt*"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">strategy</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> weighted</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">providers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">provider</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> openai</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">weight</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">80</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">provider</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> azure</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">openai</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">weight</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><br></div></code></pre></div></div>
<p>Both providers receive the same requested model name. The Azure deployment therefore needs to use the model name the application sends; routing does not translate <code>gpt-4o-mini</code> into a different deployment identifier.</p>
<p>After restarting DVARA Open Source with the updated file, the application continues to call the same endpoint:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-s</span><span class="token plain"> http://localhost:8080/v1/chat/completions </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-H</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Authorization: Bearer </span><span class="token string variable" style="color:#36acaa">$DVARA_API_KEY</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-H</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Content-Type: application/json'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-d</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">    "model": "gpt-4o-mini",</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">    "messages": [{"role": "user", "content": "Summarize this incident in two bullets."}]</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">  }'</span><br></div></code></pre></div></div>
<p>The response stays OpenAI-compatible. To verify the routing effect, use the response trace ID to inspect the structured access log; its <code>provider</code> field records which upstream handled the request. Over a meaningful request sample, the observed distribution should approach the configured weights. A handful of calls is not enough to validate a probabilistic split.</p>
<p>Open Source reads routes from <code>gateway.yaml</code> at startup, so changing a route requires a restart. DVARA Enterprise stores routes centrally and distributes updates through Flightdeck without requiring an application redeploy.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="keep-capability-filtering-separate-from-provider-preference">Keep capability filtering separate from provider preference<a href="https://dvarahq.com/blog/multi-provider-llm-routing#keep-capability-filtering-separate-from-provider-preference" class="hash-link" aria-label="Direct link to Keep capability filtering separate from provider preference" title="Direct link to Keep capability filtering separate from provider preference" translate="no">​</a></h2>
<p>A provider can be healthy and still be wrong for a particular request. Structured outputs, JSON mode, tool calls, streamed tool calls, vision, embeddings, and batch operations differ across providers.</p>
<p>Capability filtering narrows the pool before the strategy runs. This means an 80/20 weighted route is really an 80/20 split across the providers still eligible for that request. If one configured member is unavailable or lacks a required capability, it does not receive traffic merely to preserve the nominal percentage.</p>
<p>This distinction matters when interpreting routing metrics. A changed distribution can reflect health or capability filtering, not a broken weight calculation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="do-not-confuse-routing-with-failover">Do not confuse routing with failover<a href="https://dvarahq.com/blog/multi-provider-llm-routing#do-not-confuse-routing-with-failover" class="hash-link" aria-label="Direct link to Do not confuse routing with failover" title="Direct link to Do not confuse routing with failover" translate="no">​</a></h2>
<p>Routing and failover answer different questions:</p>
<table><thead><tr><th>Decision</th><th>Question</th><th>When it runs</th></tr></thead><tbody><tr><td>Routing</td><td>Which eligible provider should receive this request first?</td><td>Before the upstream call</td></tr><tr><td>Retry</td><td>Should the same provider receive another attempt?</td><td>After a retryable failure</td></tr><tr><td>Failover</td><td>Can another compatible provider safely take over?</td><td>After eligible retries or provider unavailability</td></tr></tbody></table>
<p>Retries, circuit breakers, timeouts, and fallback are enabled by default in DVARA Open Source, with configuration available per provider. They should still be tested with realistic failures. A fallback is only safe when the alternative provider supports the request and accepts the same model identifier.</p>
<p>For the full recovery path, read <strong><a class="" href="https://dvarahq.com/blog/llm-fallback-failover">LLM fallback and failover</a></strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="put-governance-before-optimization">Put governance before optimization<a href="https://dvarahq.com/blog/multi-provider-llm-routing#put-governance-before-optimization" class="hash-link" aria-label="Direct link to Put governance before optimization" title="Direct link to Put governance before optimization" translate="no">​</a></h2>
<p>Provider optimization is useful only if it preserves the controls applied to the request. A centrally routed call should still answer:</p>
<ul>
<li class="">Which workspace and API key initiated it?</li>
<li class="">Which policy decision applied before dispatch?</li>
<li class="">Was PII logged, blocked, or redacted under the configured action?</li>
<li class="">Which provider handled the call, and did a retry or fallback occur?</li>
<li class="">What usage and cost were attributed to the request?</li>
</ul>
<p>DVARA Open Source puts routing, file-configured policy, PII controls, guardrails, rate limits, and an optional local tamper-evident audit file on the same LLM request path. DVARA Enterprise adds Flightdeck, central persistence and fleet operations, advanced Enterprise controls, and the MCP and A2A Gateways.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="common-multi-provider-routing-mistakes">Common multi-provider routing mistakes<a href="https://dvarahq.com/blog/multi-provider-llm-routing#common-multi-provider-routing-mistakes" class="hash-link" aria-label="Direct link to Common multi-provider routing mistakes" title="Direct link to Common multi-provider routing mistakes" translate="no">​</a></h2>
<p><strong>Treating provider names as model mappings.</strong> A route selects a provider; it does not translate model identifiers between vendors.</p>
<p><strong>Calling weights cost optimization.</strong> A static 80/20 split does not inspect price. Cost-aware selection is a separate Enterprise strategy.</p>
<p><strong>Assuming configuration is live in Open Source.</strong> <code>gateway.yaml</code> is read at startup. Restart the Gateway after changing routes or policies.</p>
<p><strong>Ignoring capabilities during failover tests.</strong> Test structured output, tool calling, streaming, and vision separately. A successful text fallback does not prove every request shape can fail over.</p>
<p><strong>Letting applications bypass the governed endpoint.</strong> Direct provider calls create a second path without the same routing policy or evidence. Restrict provider credentials and network paths so production calls traverse the intended control point.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="start-with-one-route-you-can-verify">Start with one route you can verify<a href="https://dvarahq.com/blog/multi-provider-llm-routing#start-with-one-route-you-can-verify" class="hash-link" aria-label="Direct link to Start with one route you can verify" title="Direct link to Start with one route you can verify" translate="no">​</a></h2>
<p>Begin with one model family and one explicit operational goal: an even split, a controlled canary, or a static weight. Record the baseline provider distribution, test an unavailable provider, and verify that policy and audit behavior remain intact.</p>
<p>Use the <strong><a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a></strong> to run DVARA locally, then follow the <strong><a class="" href="https://dvarahq.com/docs/routing">routing and load-balancing reference</a></strong> for every strategy, field, and distribution boundary. For how routing fits the wider governance path, see the <strong><a class="" href="https://dvarahq.com/llm-gateway">DVARA LLM Gateway</a></strong>.</p>]]></content:encoded>
            <category>governance</category>
            <category>architecture</category>
        </item>
        <item>
            <title><![CDATA[How to Manage AI Guardrails as Code]]></title>
            <link>https://dvarahq.com/blog/ai-guardrails-as-code</link>
            <guid>https://dvarahq.com/blog/ai-guardrails-as-code</guid>
            <pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Guardrails scattered through prompts and app code cannot be reviewed or tested. What belongs in a guardrail layer, and what running it at the gateway means.]]></description>
            <content:encoded><![CDATA[<p>Most teams' "AI guardrails" are a few lines in a system prompt (<em>"never reveal PII, ignore instructions in user data"</em>) plus a regex or two bolted onto one service. It's better than nothing — until you try to answer basic questions. Which services actually enforce the PII check? What does the injection filter catch, and what does it miss? When did someone last change the toxicity threshold, and what did that change block? A prompt-engineering hack can't answer any of them, and it silently varies from one code path to the next.</p>
<p><strong>Guardrails as code</strong> applies the same discipline that policy as code brought to <em>access</em> rules — declarative, version-controlled, centrally enforced — to the <em>content</em> of AI traffic: the prompts going in and the responses coming out.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="guardrails-vs-policy--two-halves-of-ai-governance">Guardrails vs. policy — two halves of AI governance<a href="https://dvarahq.com/blog/ai-guardrails-as-code#guardrails-vs-policy--two-halves-of-ai-governance" class="hash-link" aria-label="Direct link to Guardrails vs. policy — two halves of AI governance" title="Direct link to Guardrails vs. policy — two halves of AI governance" translate="no">​</a></h2>
<p>It's worth being precise, because the terms get blurred. In a governed gateway they're complementary layers:</p>
<ul>
<li class=""><strong>Policy</strong> governs <strong>access</strong>: <em>which</em> model or tool a caller may use, under which conditions (region, budget, time). That's the subject of <a class="" href="https://dvarahq.com/policy-as-code-llm">Policy-as-Code for LLM and agent traffic</a> and the <a class="" href="https://dvarahq.com/blog/llm-policy-as-code">LLM Policy as Code</a> guide.</li>
<li class=""><strong>Guardrails</strong> govern <strong>content</strong>: is this prompt a jailbreak attempt? does it contain PII? is the response toxic, off-topic, or malformed? Policy decides <em>if the call is allowed</em>; guardrails inspect <em>what's actually in it</em>.</li>
</ul>
<p>Both run at the same control point, on every request. Both should be code, not tribal knowledge. (If you're new to the "as code" idea, start with <a class="" href="https://dvarahq.com/blog/what-is-policy-as-code">What Is Policy as Code?</a> — the principles carry over directly.)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-as-code-means-for-a-guardrail">What "as code" means for a guardrail<a href="https://dvarahq.com/blog/ai-guardrails-as-code#what-as-code-means-for-a-guardrail" class="hash-link" aria-label="Direct link to What &quot;as code&quot; means for a guardrail" title="Direct link to What &quot;as code&quot; means for a guardrail" translate="no">​</a></h2>
<p>A guardrail as code has the same four properties that define policy as code:</p>
<ul>
<li class=""><strong>Declarative</strong> — you configure <em>what</em> to detect and <em>what to do about it</em> (block, flag, redact, log), not a bespoke scanner you maintain per service.</li>
<li class=""><strong>Version-controlled</strong> — thresholds and rules have a history you can diff and roll back.</li>
<li class=""><strong>Centrally enforced</strong> — one layer scans every LLM and agent call, so a new service can't quietly skip the PII check.</li>
<li class=""><strong>Tested and audited</strong> — you can tune a threshold and see what it would catch, and every detection is recorded on the call it fired for.</li>
</ul>
<p>The last point matters most for guardrails: a blocked prompt-injection attempt or a redacted SSN is exactly the kind of event a security team needs a durable record of.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-belongs-in-the-guardrail-layer">What belongs in the guardrail layer<a href="https://dvarahq.com/blog/ai-guardrails-as-code#what-belongs-in-the-guardrail-layer" class="hash-link" aria-label="Direct link to What belongs in the guardrail layer" title="Direct link to What belongs in the guardrail layer" translate="no">​</a></h2>
<p>A practical guardrail layer covers a handful of categories, mapped closely to the <a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/" target="_blank" rel="noopener noreferrer" class="">OWASP Top 10 for LLM Applications</a>:</p>
<ul>
<li class=""><strong>Prompt injection &amp; jailbreak detection</strong> (LLM01) — pattern- and classifier-based detection of attempts to override instructions or exfiltrate the system prompt, on the way in.</li>
<li class=""><strong>PII / sensitive-data detection</strong> — layered detection: deterministic,
checksum-validated patterns for structured data (cards, SSNs, IBANs) plus NER
for unstructured identifiers (names, locations). Each hit can <strong>log</strong>,
<strong>block</strong>, or <strong>redact</strong> permanently. Commercial <strong>tokenization</strong> is the
separate request-side action for values that must be recoverable later.</li>
<li class=""><strong>Content safety</strong> — toxicity, violence, sexual content, plus your own denylists: banned topics, competitor mentions, whatever your domain requires.</li>
<li class=""><strong>Output validation</strong> — when you ask for structured output, enforce it: a response that does not match the declared JSON schema is refused with HTTP <code>422</code>. DVARA does not retry schema-validation failures automatically.</li>
<li class=""><strong>Resource limits</strong> (LLM10) — caps on input tokens, message count, and message length so a single request can't blow up cost or context.</li>
<li class=""><strong>Grounding / hallucination checks</strong> (optional) — compare response claims against provided source documents and flag ungrounded statements.</li>
</ul>
<p>The point isn't that every workload needs all of them — it's that they live in <em>one configurable layer</em> you can reason about, not scattered across prompts and middleware.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="inputs-and-outputs-including-streams">Inputs and outputs, including streams<a href="https://dvarahq.com/blog/ai-guardrails-as-code#inputs-and-outputs-including-streams" class="hash-link" aria-label="Direct link to Inputs and outputs, including streams" title="Direct link to Inputs and outputs, including streams" translate="no">​</a></h2>
<p>A prompt-only guardrail only sees half the traffic. A real guardrail layer scans <strong>both directions</strong> — the request before it reaches the model, and the response before it reaches the user — because a model can emit PII or policy-violating content the input never contained. That includes <strong>streaming</strong> responses. Observational actions can release events as they arrive. A blocking or redacting action holds the complete response, evaluates it once, and then releases or refuses it so unsafe text cannot escape between chunks.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="actions-and-per-workspace-tuning">Actions and per-workspace tuning<a href="https://dvarahq.com/blog/ai-guardrails-as-code#actions-and-per-workspace-tuning" class="hash-link" aria-label="Direct link to Actions and per-workspace tuning" title="Direct link to Actions and per-workspace tuning" translate="no">​</a></h2>
<p>Detection is only useful if you can decide what happens next. Each category resolves to an action — <strong>block</strong> the call, <strong>flag</strong> it (record and continue), <strong>redact</strong> the offending span, or <strong>log</strong> — and a risk-score threshold below which weak detections are ignored. In a multi-tenant setup, those settings tune per workspace: a regulated customer might block on any PII while an internal sandbox only logs, without either one touching application code.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-is-the-same-move-as-policy-as-code">Why this is the same move as policy as code<a href="https://dvarahq.com/blog/ai-guardrails-as-code#why-this-is-the-same-move-as-policy-as-code" class="hash-link" aria-label="Direct link to Why this is the same move as policy as code" title="Direct link to Why this is the same move as policy as code" translate="no">​</a></h2>
<p>Guardrails hard-coded into prompts and per-service middleware recreate exactly the problem access rules had before policy as code: unreviewable, untestable, unauditable, and inconsistent across code paths. Lifting them into a declarative layer at the gateway — the one point every model and agent call passes through — makes AI safety a configurable, versioned, provable property of the platform instead of a habit each team hopes it remembered.</p>
<p>That's the guardrail half of <a class="" href="https://dvarahq.com/policy-as-code-llm">Policy-as-Code for LLM and agent traffic</a>. It applies to model calls today; the same idea extends to agent tool calls — the subject of DVARA's MCP governance, where a guardrail can inspect a tool's arguments and an approval gate can pause a risky action for a human.</p>
<hr>
<p><em>Part of the Policy-as-Code cluster: start with the <a class="" href="https://dvarahq.com/policy-as-code-llm">pillar guide</a>, then <a class="" href="https://dvarahq.com/blog/what-is-policy-as-code">What Is Policy as Code?</a> and <a class="" href="https://dvarahq.com/blog/llm-policy-as-code">LLM Policy as Code</a>.</em></p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
        </item>
        <item>
            <title><![CDATA[What Is Policy as Code? A Practical Definition, with LLM Examples]]></title>
            <link>https://dvarahq.com/blog/what-is-policy-as-code</link>
            <guid>https://dvarahq.com/blog/what-is-policy-as-code</guid>
            <pubDate>Wed, 24 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Policy as code means governance rules in a declarative, version-controlled language a system enforces automatically. Where it came from and why AI needs it.]]></description>
            <content:encoded><![CDATA[<p>Every organization has policies — which resources people can access, what data can leave the building, who has to approve a risky action. The question is never <em>whether</em> you have policies. It's whether anyone can enforce them, test them, or prove what they did. When a policy lives in a document, the answer is usually no.</p>
<p><strong>Policy as code</strong> is the practice of writing those rules in a declarative, version-controlled language that a system enforces automatically — so a policy becomes a reviewable, testable, auditable artifact instead of a paragraph in a wiki nobody reads.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="policy-as-code-defined">Policy as code, defined<a href="https://dvarahq.com/blog/what-is-policy-as-code#policy-as-code-defined" class="hash-link" aria-label="Direct link to Policy as code, defined" title="Direct link to Policy as code, defined" translate="no">​</a></h2>
<p>Policy as code (PaC) is the practice of defining governance and compliance rules in machine-readable, version-controlled files, and having software evaluate and enforce them automatically at the point a decision is made — rather than documenting the rules in prose and relying on humans to remember and apply them.</p>
<p>Three things fall out of that definition, and together they're what make it "policy as <em>code</em>":</p>
<ul>
<li class=""><strong>It's declarative.</strong> You describe <em>what</em> is allowed or denied, not <em>how</em> to check it. <code>deny any model not on this allowlist</code> — not a script you maintain.</li>
<li class=""><strong>It's version-controlled.</strong> The policy is a file with a history. You can diff it, review it in a pull request, roll it back, and answer "who changed this rule, when?"</li>
<li class=""><strong>It's enforced by a system, automatically.</strong> The rule isn't advisory. A control point evaluates every relevant request against it and acts — allow, deny, warn — without a human in the loop.</li>
</ul>
<p>Take away any one of these and it stops being policy as code. A YAML file no system reads is just documentation. A script that enforces rules but lives untracked in one service is just more app code.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-it-came-from">Where it came from<a href="https://dvarahq.com/blog/what-is-policy-as-code#where-it-came-from" class="hash-link" aria-label="Direct link to Where it came from" title="Direct link to Where it came from" translate="no">​</a></h2>
<p>Policy as code isn't new — AI is just the newest place it's landing. The pattern has a decade of lineage:</p>
<ul>
<li class=""><strong>Infrastructure as code</strong> (Terraform, CloudFormation) made server and network configuration a reviewable artifact instead of a runbook of manual clicks.</li>
<li class=""><strong>Open Policy Agent (OPA)</strong> and its Rego language generalized the idea to authorization — a single engine that answers "is this action allowed?" across services.</li>
<li class=""><strong>Kubernetes admission control</strong> and <strong>CEL (Common Expression Language)</strong> brought policy evaluation into the request path itself: every API call is checked against policy before it's admitted. Envoy, Cilium, and gRPC use the same CEL engine for the same reason — it's sandboxed, deterministic, and type-checked.</li>
</ul>
<p>Each step moved rules out of scattered, imperative code and into a declarative layer a system enforces. AI governance is the same move, applied to a new kind of traffic.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="policy-as-code-vs-a-policy-document">Policy as code vs. a policy document<a href="https://dvarahq.com/blog/what-is-policy-as-code#policy-as-code-vs-a-policy-document" class="hash-link" aria-label="Direct link to Policy as code vs. a policy document" title="Direct link to Policy as code vs. a policy document" translate="no">​</a></h2>
<p>The contrast is the whole point. A policy <em>document</em> and policy <em>as code</em> can say the same words — "only approved models, no PII in prompts, log every call" — but only one of them can act on them:</p>
<table><thead><tr><th></th><th>Policy document</th><th>Policy as code</th></tr></thead><tbody><tr><td><strong>Enforced?</strong></td><td>By people, if they remember</td><td>Automatically, on every request</td></tr><tr><td><strong>Testable?</strong></td><td>No — you find out in production</td><td>Yes — simulate before it goes live</td></tr><tr><td><strong>Reversible?</strong></td><td>Rewrite and re-circulate</td><td>Roll back to a prior version instantly</td></tr><tr><td><strong>Auditable?</strong></td><td>"We have a policy"</td><td>"Here's what the policy did, per call"</td></tr></tbody></table>
<p>A document is a statement of intent. Policy as code is intent that runs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-lifecycle-that-makes-it-safe">The lifecycle that makes it safe<a href="https://dvarahq.com/blog/what-is-policy-as-code#the-lifecycle-that-makes-it-safe" class="hash-link" aria-label="Direct link to The lifecycle that makes it safe" title="Direct link to The lifecycle that makes it safe" translate="no">​</a></h2>
<p>Writing rules as code is only half the value; the other half is being able to <em>change</em> them without fear. Mature policy as code moves a change through stages, the same way you'd ship any production change:</p>
<ol>
<li class=""><strong>Author</strong> — write the rule; it starts as a draft, affecting nothing.</li>
<li class=""><strong>Test</strong> — dry-run it against real inputs and see the exact decision before it touches live traffic.</li>
<li class=""><strong>Shadow</strong> — run the candidate in production <em>in parallel</em>, recording what it <em>would</em> have done, while changing nothing.</li>
<li class=""><strong>Promote</strong> — activate it, with conflict detection to catch rules that contradict each other.</li>
<li class=""><strong>Roll back</strong> — every change is versioned; revert instantly if it misbehaves.</li>
</ol>
<p>Without this, "policy as code" is just a config file you're afraid to touch. With it, governance changes become routine and low-risk.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="policy-as-code-for-llm-and-agent-traffic">Policy as code for LLM and agent traffic<a href="https://dvarahq.com/blog/what-is-policy-as-code#policy-as-code-for-llm-and-agent-traffic" class="hash-link" aria-label="Direct link to Policy as code for LLM and agent traffic" title="Direct link to Policy as code for LLM and agent traffic" translate="no">​</a></h2>
<p>For AI, the rules are about model and tool calls rather than servers or Kubernetes objects. Typical LLM policies answer questions like these:</p>
<table><thead><tr><th>Question</th><th>Rule</th></tr></thead><tbody><tr><td>Which models may this application call?</td><td>Model allowlist or denylist</td></tr><tr><td>How large an answer may it ask for?</td><td><code>max_tokens</code> ceiling</td></tr><tr><td>Which functions may it offer the model?</td><td>Tool allowlist or denylist</td></tr><tr><td>Should a preview model be flagged rather than blocked?</td><td>Warn instead of deny</td></tr><tr><td>Which MCP server, tool or argument may an agent use?</td><td>MCP conditions</td></tr></tbody></table>
<p>A rule is a few lines of YAML. This one blocks an unapproved model and tells the caller what to use instead:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">rules</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> deny</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">legacy</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">priority</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">conditions</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">model</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">denylist</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">mock/legacy</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">action</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> DENY</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">deny_message</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"This model is not approved. Use mock/approved instead."</span><br></div></code></pre></div></div>
<p>The gateway checks every request against the rule before it reaches the provider, and records the decision in the audit log. Content checks such as PII detection or prompt-injection screening are guardrails, not access policy: they inspect the text rather than the request's facts, so they are configured separately.</p>
<p><a class="" href="https://dvarahq.com/blog/llm-policy-as-code">LLM Policy as Code</a> walks through writing, testing and rolling out rules like this one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-ai-teams-need-it-now">Why AI teams need it now<a href="https://dvarahq.com/blog/what-is-policy-as-code#why-ai-teams-need-it-now" class="hash-link" aria-label="Direct link to Why AI teams need it now" title="Direct link to Why AI teams need it now" translate="no">​</a></h2>
<p>AI workloads broke the old assumption that governance can live in application code. Model calls and agent (MCP) tool calls carry decisions — which model, how many tokens, which tools, which region, whose budget — that a general-purpose API gateway or IAM system can't even see, let alone govern. Hard-coding those rules across every service that calls a model recreates exactly the ungovernable mess that infrastructure as code was invented to solve.</p>
<p>The answer is the same: express the rules once, in a declarative language, and enforce them at one control point every LLM and agent request already passes through. That's what <a class="" href="https://dvarahq.com/policy-as-code-llm">Policy-as-Code for LLM and agent traffic</a> is — and if you want the concrete rule model (YAML conditions, CEL expressions, the allow/deny/warn actions), the companion guide <strong><a class="" href="https://dvarahq.com/blog/llm-policy-as-code">LLM Policy as Code</a></strong> walks through it end to end.</p>
<hr>
<p><em>Start with the <a class="" href="https://dvarahq.com/policy-as-code-llm">Policy-as-Code pillar guide</a> for the full picture, then go deep on the rule model in <a class="" href="https://dvarahq.com/blog/llm-policy-as-code">LLM Policy as Code</a>.</em></p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
        </item>
        <item>
            <title><![CDATA[LLM Policy as Code: Rules, Enforcement, and Rollout]]></title>
            <link>https://dvarahq.com/blog/llm-policy-as-code</link>
            <guid>https://dvarahq.com/blog/llm-policy-as-code</guid>
            <pubDate>Tue, 23 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how LLM policy as code turns model, token, and tool rules into centrally enforced decisions with audit evidence and a safer rollout process.]]></description>
            <content:encoded><![CDATA[<p><strong>LLM policy as code</strong> expresses model, token, and tool rules in a declarative, version-controlled format and enforces them at a shared control point before a provider receives the request. It replaces scattered application checks with one reviewable answer to “what is allowed, for whom, and what happened when the rule matched?”</p>
<p>DVARA is an AI gateway that governs model, tool and agent-to-agent calls; the <strong>DVARA LLM Gateway</strong> is the component that enforces LLM policy on the request path. The policy is useful because enforcement and evidence travel together—not merely because the rules are written in YAML.</p>
<!-- -->
<p><em>Updated September 24, 2026 for DVARA 1.8.0.</em></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-llm-policy-as-code">What is LLM policy as code?<a href="https://dvarahq.com/blog/llm-policy-as-code#what-is-llm-policy-as-code" class="hash-link" aria-label="Direct link to What is LLM policy as code?" title="Direct link to What is LLM policy as code?" translate="no">​</a></h2>
<p>LLM policy as code is a way to define AI-traffic violations as machine-readable rules, keep those rules under change control, and evaluate them consistently before model dispatch. A matching rule can deny the call or warn the caller while allowing it to continue.</p>
<p>The implementation has three parts:</p>
<ol>
<li class=""><strong>A policy artifact</strong> that names the rule, its priority, matching conditions, action, and an operator-written message.</li>
<li class=""><strong>A central enforcement point</strong> that sees AI-specific request attributes such as the model, requested output tokens, and offered tools.</li>
<li class=""><strong>An evidence record</strong> that identifies the policy and rule involved in each decision.</li>
</ol>
<p>Without central enforcement, a YAML file is only documentation. Without a reviewable artifact, central enforcement becomes opaque application logic. Without evidence, neither one proves what happened on a particular request.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-should-policy-govern">What should policy govern?<a href="https://dvarahq.com/blog/llm-policy-as-code#what-should-policy-govern" class="hash-link" aria-label="Direct link to What should policy govern?" title="Direct link to What should policy govern?" translate="no">​</a></h2>
<p>Use policy for deterministic access and usage decisions that can be evaluated from request context.</p>
<table><thead><tr><th>Question</th><th>Policy pattern</th></tr></thead><tbody><tr><td>Which models may this application call?</td><td>Model allowlist or denylist</td></tr><tr><td>How large may an explicitly requested output be?</td><td><code>max_tokens</code> limit</td></tr><tr><td>Which functions may this application offer to a model?</td><td>Tool allowlist or denylist</td></tr><tr><td>Should a preview model warn rather than block?</td><td><code>WARN_AGENT</code> action</td></tr><tr><td>Which region, time window, or budget state is allowed?</td><td>Enterprise conditions</td></tr><tr><td>Which MCP server, tool, or argument is allowed?</td><td>Enterprise MCP conditions on the MCP plane</td></tr></tbody></table>
<p>Keep probabilistic content inspection separate. Prompt-injection detection, content filtering, and PII detection are guardrail or data-protection controls; they should not be disguised as deterministic access-policy rules.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="write-one-policy-that-fails-clearly">Write one policy that fails clearly<a href="https://dvarahq.com/blog/llm-policy-as-code#write-one-policy-that-fails-clearly" class="hash-link" aria-label="Direct link to Write one policy that fails clearly" title="Direct link to Write one policy that fails clearly" translate="no">​</a></h2>
<p>This DVARA Open Source policy denies one unapproved model. Put it in <code>gateway.yaml</code> alongside the workspace, route, and API-key fingerprint from the <a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">policies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">id</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> approved</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">models</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">only</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">workspace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">status</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ACTIVE</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">dsl</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      version: "1"</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">      rules:</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        - id: deny-legacy-model</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          priority: 10</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          conditions:</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">            model:</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">              denylist: [mock/legacy]</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          action: DENY</span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          deny_message: "This model is not approved. Use mock/approved instead."</span><br></div></code></pre></div></div>
<p>Restart DVARA Open Source after changing <code>gateway.yaml</code>; the file is read at startup. Then send a request for the denied model:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-s</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-w</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'\nHTTP %{http_code}\n'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  http://localhost:8080/v1/chat/completions </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-H</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Authorization: Bearer </span><span class="token string variable" style="color:#36acaa">$DVARA_API_KEY</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-H</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Content-Type: application/json'</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-d</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">    "model": "mock/legacy",</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">    "messages": [{"role": "user", "content": "Draft a customer reply."}]</span><br></div><div class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">  }'</span><br></div></code></pre></div></div>
<p>The Gateway stops the request before provider dispatch and returns the safe alternative written into the rule:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"error"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"message"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"This model is not approved. Use mock/approved instead."</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"type"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"policy_violation"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"code"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"policy_denied"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"trace_id"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"01K4V5R9X21MDN8W5A6E7ZQ3TC"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">HTTP </span><span class="token number" style="color:#36acaa">403</span><br></div></code></pre></div></div>
<p>When the local audit file is configured, the same decision is recorded as <code>POLICY_DENIED</code> with the policy ID, rule ID, workspace, reason, and chain-integrity fields. The trace ID changes on every request; use the value from the actual response when investigating a call.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-conditions-as-violations">Read conditions as violations<a href="https://dvarahq.com/blog/llm-policy-as-code#read-conditions-as-violations" class="hash-link" aria-label="Direct link to Read conditions as violations" title="Direct link to Read conditions as violations" translate="no">​</a></h2>
<p>DVARA policy conditions describe when a rule should fire, not the desired steady state. For example, a model <code>allowlist</code> matches when the requested model is <strong>outside</strong> the list; the <code>DENY</code> action then blocks the violation.</p>
<p>Four rules prevent most policy mistakes:</p>
<ul>
<li class="">Multiple conditions inside one rule use <strong>AND</strong>. Put independent denial reasons in separate rules.</li>
<li class="">Model names are exact and case-sensitive, not prefixes or globs. List every approved dated model name explicitly.</li>
<li class="">A <code>max_tokens</code> rule evaluates an explicit request value. If the request omits <code>max_tokens</code>, that condition does not match or inject a default.</li>
<li class="">Empty conditions are refused rather than accepted as policies that appear active but check nothing.</li>
</ul>
<p>Write denial messages for the caller, not the policy author. “Use <code>mock/approved</code> instead” is more useful than “rule 10 failed.”</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="choose-between-deny-and-warn">Choose between deny and warn<a href="https://dvarahq.com/blog/llm-policy-as-code#choose-between-deny-and-warn" class="hash-link" aria-label="Direct link to Choose between deny and warn" title="Direct link to Choose between deny and warn" translate="no">​</a></h2>
<p><code>DENY</code> stops the matching request before provider dispatch. Use it for a control whose violation must not reach the model provider.</p>
<p><code>WARN_AGENT</code> allows the call to continue and attaches a governance warning to the result and audit trail. Use it when the calling application is expected to react but blocking would be premature, such as a preview-model migration period.</p>
<p>A warning is not a softer spelling of enforcement. If the caller ignores warnings, the control is observational. Define who consumes the warning and what that system does before relying on it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="separate-open-source-enforcement-from-enterprise-lifecycle">Separate Open Source enforcement from Enterprise lifecycle<a href="https://dvarahq.com/blog/llm-policy-as-code#separate-open-source-enforcement-from-enterprise-lifecycle" class="hash-link" aria-label="Direct link to Separate Open Source enforcement from Enterprise lifecycle" title="Direct link to Separate Open Source enforcement from Enterprise lifecycle" translate="no">​</a></h2>
<p>The YAML language is shared, but the available conditions and operating workflow differ.</p>
<table><thead><tr><th>Capability</th><th>DVARA Open Source</th><th>DVARA Enterprise</th></tr></thead><tbody><tr><td>Model allowlist/denylist</td><td>Yes</td><td>Yes</td></tr><tr><td>Explicit <code>max_tokens</code> ceiling</td><td>Yes</td><td>Yes</td></tr><tr><td>LLM-request tool allowlist/denylist</td><td>Yes</td><td>Yes</td></tr><tr><td>Active policy enforcement</td><td>Yes</td><td>Yes</td></tr><tr><td>Draft, Shadow, Archived, dry-run, version history, rollback</td><td>No</td><td>Yes</td></tr><tr><td>Data-residency, time-of-day, budget conditions</td><td>No</td><td>Yes</td></tr><tr><td>CEL expressions</td><td>No</td><td>Yes</td></tr><tr><td>MCP server, tool, and argument conditions</td><td>No</td><td>Yes, on the MCP plane</td></tr><tr><td>Central editing and fleet distribution through Flightdeck</td><td>No</td><td>Yes</td></tr></tbody></table>
<p>DVARA Open Source 1.8.0 evaluates <code>ACTIVE</code> policies from <code>gateway.yaml</code>. It does not evaluate Shadow policies or provide CEL, data-residency, time-of-day, budget-utilization, or MCP conditions. Unsupported conditions are refused rather than silently ignored.</p>
<p>DVARA Enterprise adds the managed lifecycle: author a Draft, dry-run the fields the test surface can represent, observe representative traffic in Shadow, promote to Active, and roll back by restoring an earlier version as a new revision. Enterprise production rights and support require a licence.</p>
<blockquote>
<p><strong>Updated for 1.8.2.</strong> This post originally said the licence activates the MCP and A2A planes. In 1.8.2 every feature, the MCP and A2A planes included, runs without a licence for non-production use, within 3 workspaces and 100,000 governed calls a month. A licence removes both limits and gives production rights and support. An expired licence refuses no traffic.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="roll-out-policy-without-creating-an-outage">Roll out policy without creating an outage<a href="https://dvarahq.com/blog/llm-policy-as-code#roll-out-policy-without-creating-an-outage" class="hash-link" aria-label="Direct link to Roll out policy without creating an outage" title="Direct link to Roll out policy without creating an outage" translate="no">​</a></h2>
<p>A reliable rollout is operational, not merely syntactic:</p>
<ol>
<li class=""><strong>Name the violation.</strong> Write one rule for one independently actionable reason.</li>
<li class=""><strong>Use representative requests.</strong> Include allowed, denied, missing-field, tool-calling, and streaming cases relevant to the rule.</li>
<li class=""><strong>Check the error contract.</strong> The caller needs a stable status, code, trace ID, and useful remediation message.</li>
<li class=""><strong>Verify evidence.</strong> Confirm that the decision names the expected policy, rule, workspace, and reason.</li>
<li class=""><strong>Bound the rollout.</strong> In Enterprise, use Draft, dry-run, and Shadow before Active. In Open Source, test the file against a non-production Gateway before restarting production.</li>
<li class=""><strong>Plan reversal.</strong> Keep the last known-good file or Enterprise version and rehearse the rollback path.</li>
</ol>
<p>Dry-run and Shadow are not equivalent. Dry-run evaluates a synthetic input and only the fields the test surface supplies. Shadow observes real request context without changing the live decision. Conditions involving tools, time, region, budgets, or MCP context need representative governed traffic rather than assumptions from a simple model-only dry-run.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="policy-as-code-is-not-application-authorization">Policy as code is not application authorization<a href="https://dvarahq.com/blog/llm-policy-as-code#policy-as-code-is-not-application-authorization" class="hash-link" aria-label="Direct link to Policy as code is not application authorization" title="Direct link to Policy as code is not application authorization" translate="no">​</a></h2>
<p>IAM still determines who can authenticate to applications and administrative surfaces. Application authorization still decides which business operation a user may perform. LLM policy governs the AI request at the shared enforcement point.</p>
<p>These layers should reinforce one another, not substitute for one another. A tool policy can prevent an application from offering <code>cancel_order</code> to a model; it does not decide whether the signed-in human is authorized to cancel order <code>ORD-1048</code> inside the order service.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="common-llm-policy-as-code-mistakes">Common LLM policy-as-code mistakes<a href="https://dvarahq.com/blog/llm-policy-as-code#common-llm-policy-as-code-mistakes" class="hash-link" aria-label="Direct link to Common LLM policy-as-code mistakes" title="Direct link to Common LLM policy-as-code mistakes" translate="no">​</a></h2>
<p><strong>Writing broad rules with unclear messages.</strong> Separate model, token, and tool violations so each response tells the caller what to fix.</p>
<p><strong>Assuming an allowlist matches allowed values.</strong> It matches the violation—values outside the list—so the associated action can deny them.</p>
<p><strong>Describing Enterprise lifecycle as an Open Source feature.</strong> Open Source reads active file-based policy at startup. Draft, Shadow, dry-run, version history, rollback, CEL, and advanced conditions belong to Enterprise.</p>
<p><strong>Forgetting the bypass path.</strong> A centrally enforced rule governs only requests that traverse the Gateway. Restrict direct provider credentials and network paths accordingly.</p>
<p><strong>Treating an audit record as proof that storage cannot change.</strong> The Open Source audit file is tamper-evident through HMAC signing and hash chaining. Someone with direct filesystem access can still delete data; the mechanism makes changes detectable rather than impossible.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="start-with-one-model-rule-and-one-observable-result">Start with one model rule and one observable result<a href="https://dvarahq.com/blog/llm-policy-as-code#start-with-one-model-rule-and-one-observable-result" class="hash-link" aria-label="Direct link to Start with one model rule and one observable result" title="Direct link to Start with one model rule and one observable result" translate="no">​</a></h2>
<p>The fastest useful policy is small: deny one model, send one allowed request and one denied request, then verify the error and audit record. Expand only after the team can explain the behavior and reversal path.</p>
<p>Use the <strong><a class="" href="https://dvarahq.com/docs/open-source-quickstart">Open Source quickstart</a></strong> for a local governed request, the <strong><a class="" href="https://dvarahq.com/docs/policy-as-code">Policy-as-Code guide</a></strong> for the complete rollout, and the <strong><a class="" href="https://dvarahq.com/docs/policy-condition-reference">policy condition reference</a></strong> for exact matching behavior. For product-level evaluation, see <strong><a class="" href="https://dvarahq.com/policy-as-code-llm">Policy-as-Code for LLM traffic</a></strong>.</p>]]></content:encoded>
            <category>governance</category>
            <category>security</category>
        </item>
    </channel>
</rss>