<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nico</title>
    <description>The latest articles on DEV Community by Nico (@ndreno).</description>
    <link>https://dev.to/ndreno</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3904839%2Fa5266afe-04b3-485a-8976-4c493aca11b0.png</url>
      <title>DEV Community: Nico</title>
      <link>https://dev.to/ndreno</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ndreno"/>
    <language>en</language>
    <item>
      <title>Barbacane vs Portkey and LiteLLM: picking an AI gateway in 2026</title>
      <dc:creator>Nico</dc:creator>
      <pubDate>Mon, 18 May 2026 14:34:38 +0000</pubDate>
      <link>https://dev.to/ndreno/barbacane-vs-portkey-and-litellm-picking-an-ai-gateway-in-2026-48kn</link>
      <guid>https://dev.to/ndreno/barbacane-vs-portkey-and-litellm-picking-an-ai-gateway-in-2026-48kn</guid>
      <description>&lt;p&gt;&lt;em&gt;If you are picking an AI gateway in 2026, Portkey, LiteLLM, and Barbacane are all real options. They overlap enough to make the choice real, and they differ enough that the right answer depends on what else you want your gateway to do.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every AI-gateway evaluation runs into the same question after the first demo: once your OpenAI calls go through a gateway, what about everything else? The rate limits your platform team owns, the auth your security team owns, the audit trail your compliance team owns, the spec-first workflow your API team relies on, the agents calling back the other way. The more of that lives next to the AI traffic, the more the choice of AI gateway becomes an architecture decision and not a feature match.&lt;/p&gt;

&lt;p&gt;This post compares the three products on that axis. What they share. What separates them. How to pick.&lt;/p&gt;




&lt;h3&gt;
  
  
  The overlap: outbound LLM proxying
&lt;/h3&gt;

&lt;p&gt;All three products sit between your application and one or more LLM providers. All three give you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provider abstraction&lt;/strong&gt; with an OpenAI-compatible API surface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback chains&lt;/strong&gt; when a provider errors, times out, or is unreachable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token usage and latency metrics&lt;/strong&gt; per call and per provider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget and rate-limit guardrails&lt;/strong&gt; at the gateway layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt and response guardrails&lt;/strong&gt; (scope varies by product)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If outbound LLM proxy is all you need, all three will work. The differences show up in what else the gateway does, how it is configured, and what happens when your requirements grow beyond the LLM path.&lt;/p&gt;




&lt;h3&gt;
  
  
  What Portkey is
&lt;/h3&gt;

&lt;p&gt;Portkey is a commercial AI gateway, available as managed SaaS or self-hosted. It focuses specifically on the LLM path and invests heavily in the operator experience: a configuration UI, a playground, a prompt library, an observability dashboard purpose-built for LLM traffic. It tends to be the right pick if you want an AI gateway as a product (vendor support, managed upgrades, fancy UI) and AI is the thing your team cares about most.&lt;/p&gt;

&lt;h3&gt;
  
  
  What LiteLLM is
&lt;/h3&gt;

&lt;p&gt;LiteLLM is an open-source Python proxy that exposes a very broad set of LLM providers behind one unified OpenAI-compatible API. Actively developed, wide provider coverage, can run as a Python library or as a proxy server. Good pick if you want broad provider support, an MIT-licensed OSS foundation, and a Python-native runtime that plays well with your ML tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Barbacane is
&lt;/h3&gt;

&lt;p&gt;Barbacane is an open-source, Rust-native API gateway. AI capability is built from composable plugins rather than a monolithic feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ai-proxy&lt;/code&gt; dispatcher&lt;/strong&gt; routes requests to OpenAI, Anthropic, and Ollama (plus any OpenAI-compatible endpoint: vLLM, TGI, LocalAI, Azure). The client always sends OpenAI format; the dispatcher translates per provider, pins the provider API version, and handles SSE streaming where the provider supports it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Named targets + &lt;code&gt;cel&lt;/code&gt; middleware&lt;/strong&gt; express policy-driven routing. A target like &lt;code&gt;premium&lt;/code&gt; is a full provider profile (provider, model, credentials); the &lt;code&gt;cel&lt;/code&gt; middleware writes &lt;code&gt;ai.target&lt;/code&gt; into the request context when a rule matches, and the dispatcher picks the target from there. Credentials never leave dispatcher config.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ai-prompt-guard&lt;/code&gt;, &lt;code&gt;ai-token-limit&lt;/code&gt;, &lt;code&gt;ai-cost-tracker&lt;/code&gt;, &lt;code&gt;ai-response-guard&lt;/code&gt;&lt;/strong&gt; middlewares compose around the dispatcher. Each is a separate, skippable concern with named profiles, CEL expressions, and fail-closed defaults on misconfig.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one more capability Portkey and LiteLLM do not offer: Barbacane is also an &lt;a href="https://barbacane.dev/ai/" rel="noopener noreferrer"&gt;MCP gateway&lt;/a&gt;. The same artifact that proxies your LLM traffic outbound also exposes your existing APIs to AI agents as tools inbound. One gateway covers both directions of AI traffic.&lt;/p&gt;




&lt;h3&gt;
  
  
  The architectural difference: monolithic AI proxy vs dispatcher plus middlewares
&lt;/h3&gt;

&lt;p&gt;This is where the three products diverge.&lt;/p&gt;

&lt;p&gt;Portkey and LiteLLM treat the AI gateway as a unified product: one binary, one config, one API surface. Every operational concern (rate limits, caching, observability, guardrails) is a feature baked into the proxy. This is the right shape when AI is the only traffic the gateway handles.&lt;/p&gt;

&lt;p&gt;Barbacane treats the AI gateway as a set of primitives you compose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;ai-proxy&lt;/code&gt; dispatcher handles translation and routing.&lt;/li&gt;
&lt;li&gt;Each concern is a separate middleware, ordered explicitly in the spec.&lt;/li&gt;
&lt;li&gt;You stack the middlewares you need, skip the ones you do not, and compose multiple instances of the same plugin (stack two &lt;code&gt;ai-token-limit&lt;/code&gt; instances for a minute-and-hour window, stack multiple &lt;code&gt;cel&lt;/code&gt; rules for routing).&lt;/li&gt;
&lt;li&gt;The exact same primitives govern non-AI traffic on the same gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trade-off is sharp. If you want the shortest path from zero to "OpenAI call via a gateway", Portkey and LiteLLM win on time-to-live. If you want AI traffic governed the same way your team already governs every other HTTP request, Barbacane's composition model gets you there without a second product to run, a second config source to reconcile, or a second telemetry stack to watch.&lt;/p&gt;

&lt;p&gt;The architectural bet is the same one the service-mesh community made five years ago: specialized proxies for specialized traffic, or one data plane that handles every protocol your platform cares about. Both are valid; they produce different operational footprints.&lt;/p&gt;




&lt;h3&gt;
  
  
  Spec-first: OpenAPI as source of truth
&lt;/h3&gt;

&lt;p&gt;Portkey and LiteLLM configure AI routes in their own config files (YAML for LiteLLM, config UI or SDK for Portkey). Barbacane configures AI routes in your OpenAPI spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/v1/chat/completions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chatCompletion&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Route LLM chat completion requests&lt;/span&gt;
      &lt;span class="na"&gt;x-barbacane-dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ai-proxy&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openai&lt;/span&gt;
          &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gpt-4o&lt;/span&gt;
          &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${OPENAI_API_KEY}"&lt;/span&gt;
          &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;claude-sonnet-4-20250514&lt;/span&gt;
              &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${ANTHROPIC_API_KEY}"&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ollama&lt;/span&gt;
              &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;llama3&lt;/span&gt;
              &lt;span class="na"&gt;base_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://ollama:11434&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation your frontend team reads, the client SDKs they generate, the contracts your platform team enforces, and the gateway config your SRE team operates all derive from the same file. Adding an LLM route adds an entry in the spec. Renaming a parameter renames it everywhere. Vacuum-based lint runs shift-left in your editor, in a pre-commit hook, or in CI, so provider typos and invalid regex patterns fail at lint time, not at call time.&lt;/p&gt;

&lt;p&gt;If your organization is already spec-first for non-AI APIs, extending that discipline to AI routes is the cheapest integration path. If you do not run spec-first APIs, Portkey and LiteLLM feel more familiar because they do not ask you to change your workflow.&lt;/p&gt;




&lt;h3&gt;
  
  
  The inbound direction: MCP
&lt;/h3&gt;

&lt;p&gt;One axis Portkey and LiteLLM do not compete on.&lt;/p&gt;

&lt;p&gt;Portkey and LiteLLM sit between your application and the LLM. They do not stand between an AI agent and your APIs. That inbound direction is a different gateway category; we covered it at length in the &lt;a href="https://barbacane.dev/blog/what-is-an-mcp-gateway/" rel="noopener noreferrer"&gt;canonical MCP gateway post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Barbacane is a full MCP gateway in addition to its outbound AI capability. One artifact handles both directions. Whether that matters depends on whether agents calling your APIs is in scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you are building an agent product and your agents only hit public tools and third-party services, the inbound direction does not apply and the MCP capability is not doing work for you.&lt;/li&gt;
&lt;li&gt;If your agents call your internal APIs, or if you are a platform team preparing to expose internal APIs to agents built elsewhere, the inbound direction is real work. Barbacane treats it as a first-class concern. Portkey and LiteLLM leave it outside the gateway entirely, which means a separate MCP server per service and all the sprawl the canonical post describes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  When to pick which
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Pick&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fastest path from zero to an OpenAI call via a gateway, with an operator UI&lt;/td&gt;
&lt;td&gt;Portkey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Very broad LLM provider coverage, Python-native, OSS-first&lt;/td&gt;
&lt;td&gt;LiteLLM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Managed SaaS with vendor support and a polished dashboard&lt;/td&gt;
&lt;td&gt;Portkey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI gateway as part of a broader API gateway, not a second box&lt;/td&gt;
&lt;td&gt;Barbacane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI routes defined in your OpenAPI spec alongside the rest of your API&lt;/td&gt;
&lt;td&gt;Barbacane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same gateway also exposes your APIs to AI agents via MCP&lt;/td&gt;
&lt;td&gt;Barbacane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSS, self-hostable, Rust-native, FIPS-ready for regulated-industry posture&lt;/td&gt;
&lt;td&gt;Barbacane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform team; AI is one of many gateway concerns (auth, routing, observability)&lt;/td&gt;
&lt;td&gt;Barbacane&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-first product team; LLM calls are the only traffic the gateway proxies&lt;/td&gt;
&lt;td&gt;Portkey or LiteLLM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Feature comparison
&lt;/h3&gt;

&lt;p&gt;A compact, direction-setting comparison. All three products evolve; check current docs before committing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Portkey&lt;/th&gt;
&lt;th&gt;LiteLLM&lt;/th&gt;
&lt;th&gt;Barbacane&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Outbound LLM proxy&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;ai-proxy&lt;/code&gt; dispatcher)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound MCP gateway&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider coverage&lt;/td&gt;
&lt;td&gt;Broad&lt;/td&gt;
&lt;td&gt;Very broad (100+ models)&lt;/td&gt;
&lt;td&gt;OpenAI, Anthropic, Ollama, plus any OpenAI-compat API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider fallback&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy-driven routing&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (via &lt;code&gt;cel&lt;/code&gt; middleware + named targets)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt and response guardrails&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ai-prompt-guard&lt;/code&gt; + &lt;code&gt;ai-response-guard&lt;/code&gt; middlewares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token rate limits&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;Built in&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ai-token-limit&lt;/code&gt; middleware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost tracking&lt;/td&gt;
&lt;td&gt;Built-in dashboard&lt;/td&gt;
&lt;td&gt;Built-in metrics&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ai-cost-tracker&lt;/code&gt; middleware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source of truth for config&lt;/td&gt;
&lt;td&gt;Config UI or SDK&lt;/td&gt;
&lt;td&gt;YAML config&lt;/td&gt;
&lt;td&gt;OpenAPI spec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;td&gt;SaaS and self-host&lt;/td&gt;
&lt;td&gt;Python proxy&lt;/td&gt;
&lt;td&gt;Rust binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;AGPLv3 + commercial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Governs non-AI HTTP traffic&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (full API gateway)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Where a row says "No", the product was not designed for that concern. Forcing a tool into the wrong role is how shadow stacks start.&lt;/p&gt;




&lt;h3&gt;
  
  
  What to watch for during procurement
&lt;/h3&gt;

&lt;p&gt;If you are being pitched an AI gateway and the first question is "do you already run an API gateway?", you are in the right conversation. If it is not asked, ask it yourself. The answer changes what you need from the new product.&lt;/p&gt;

&lt;p&gt;A short procurement checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Where does AI gateway config live?&lt;/strong&gt; If the answer is "a second config file", you are creating a drift source. Prefer products that integrate with the spec or config surface your team already uses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the feature set monolithic or composable?&lt;/strong&gt; Monolithic is simpler day one and harder to extend. Composable is more to learn and easier to shape to your operational model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it govern agent traffic too?&lt;/strong&gt; If agents calling your APIs is on your roadmap, ask about MCP. If not, skip.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How does it integrate with your observability stack?&lt;/strong&gt; Prometheus, OpenTelemetry, structured logs. Avoid products that ship their own telemetry you have to separately consume.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosting path and license.&lt;/strong&gt; SaaS is fine for many teams; regulated, on-prem, or air-gapped environments will need an OSS, self-hostable option.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Closing thoughts
&lt;/h3&gt;

&lt;p&gt;All three products handle the core outbound LLM path competently. The axis that differentiates them is how the AI gateway relates to the rest of your infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If AI is the primary problem and the AI gateway stands alone, &lt;strong&gt;Portkey or LiteLLM&lt;/strong&gt; will get you live faster. Pick Portkey if you want SaaS with a UI. Pick LiteLLM if you want OSS breadth and a Python runtime.&lt;/li&gt;
&lt;li&gt;If AI is one of several gateway concerns and you want one spec-first artifact covering auth, rate limits, routing, AI, and MCP, &lt;strong&gt;Barbacane&lt;/strong&gt; is the architecture fit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick by architecture, not feature count. The feature sets will converge; the architectural assumptions will not.&lt;/p&gt;

&lt;p&gt;For the Barbacane side of the comparison, &lt;a href="https://barbacane.dev/ai/" rel="noopener noreferrer"&gt;the /ai page&lt;/a&gt; is the five-minute version, and the &lt;a href="https://barbacane.dev/blog/what-is-an-mcp-gateway/" rel="noopener noreferrer"&gt;canonical MCP gateway post&lt;/a&gt; is the longer read. For Portkey and LiteLLM, their own docs are the right place to start; their positioning is consistent enough that a fair comparison is easier now than it was a year ago.&lt;/p&gt;

</description>
      <category>aigateway</category>
      <category>mcpgateway</category>
      <category>portkey</category>
      <category>litellm</category>
    </item>
    <item>
      <title>Why agents break where developers cope: API governance as agent readiness</title>
      <dc:creator>Nico</dc:creator>
      <pubDate>Tue, 12 May 2026 15:48:33 +0000</pubDate>
      <link>https://dev.to/ndreno/why-agents-break-where-developers-cope-api-governance-as-agent-readiness-1pop</link>
      <guid>https://dev.to/ndreno/why-agents-break-where-developers-cope-api-governance-as-agent-readiness-1pop</guid>
      <description>&lt;p&gt;&lt;em&gt;Every API team has a list of things they keep meaning to fix. Agents are about to decide which of those things are actually optional.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you have worked on an internal API platform for any length of time, you know the inventory. The endpoint that returns &lt;code&gt;200&lt;/code&gt; with an error body instead of &lt;code&gt;4xx&lt;/code&gt;. The field that is documented as required and is, in practice, sometimes null. The auth header that is technically optional because one legacy caller never adopted the new flow. The OpenAPI spec that is mostly right, drifting from reality at the edges. The rate-limit response that returns a different shape on the staging cluster than in production.&lt;/p&gt;

&lt;p&gt;None of this stops anyone from shipping. Human developers absorb it. They read the issue tracker, ask in Slack, copy a working example from another service, and move on. The API works, in the sense that the people calling it have learned how to call it.&lt;/p&gt;

&lt;p&gt;Then agents show up, and the bill comes due.&lt;/p&gt;




&lt;h3&gt;
  
  
  What human developers absorb
&lt;/h3&gt;

&lt;p&gt;Most internal APIs are held together by a layer of unwritten knowledge. Some of it is documented, most of it is not, and a meaningful slice is contradictory. Developers cope by reading source code, copying from working clients, asking the team that owns the service, and pattern-matching from other APIs they have used.&lt;/p&gt;

&lt;p&gt;A short, non-exhaustive list of things human developers routinely route around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec drift.&lt;/strong&gt; The OpenAPI file says one thing, the runtime returns another. Devs notice the divergence, log a ticket, and update their client by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field shape inconsistency.&lt;/strong&gt; &lt;code&gt;created_at&lt;/code&gt; is a Unix timestamp on one endpoint, an ISO string on another, both in the same service. The dev writes a small adapter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent error contracts.&lt;/strong&gt; Some endpoints return RFC 9457 problem details, some return a custom envelope, some return a string. Each is fine if you know about it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth quirks.&lt;/strong&gt; The endpoint accepts either a JWT in &lt;code&gt;Authorization&lt;/code&gt; or a session cookie, but only the JWT path enforces the scope check. Nobody documents this; everyone who matters knows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undocumented side effects.&lt;/strong&gt; Calling &lt;code&gt;POST /orders&lt;/code&gt; also triggers a webhook to billing. The doc does not mention it; the integration tests imply it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent pagination.&lt;/strong&gt; Some endpoints use cursor pagination, some use offset, some return the whole list because "it is a small table." Until it is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limit signals.&lt;/strong&gt; One service returns &lt;code&gt;429&lt;/code&gt; with &lt;code&gt;Retry-After&lt;/code&gt;, another returns &lt;code&gt;503&lt;/code&gt;, another returns &lt;code&gt;200&lt;/code&gt; with an empty list. Backoff logic is written defensively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operation IDs and descriptions.&lt;/strong&gt; Often missing, sometimes copy-pasted, occasionally lying. Developers ignore the field and read the path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of these has a workaround. The workaround lives in someone's head, or in a wrapper library, or in a runbook, or in the code of the first team that integrated and figured it out. The API surface and the API contract are two different things, and human developers spend a non-trivial fraction of their time reconciling them.&lt;/p&gt;

&lt;p&gt;This has been tolerable for fifteen years because the cost of friction is bounded by the patience of the human on the other end. The pattern works until the other end stops being human.&lt;/p&gt;




&lt;h3&gt;
  
  
  What agents cannot absorb
&lt;/h3&gt;

&lt;p&gt;An LLM-driven agent is not a slower developer. It is a different kind of consumer, and the differences matter.&lt;/p&gt;

&lt;p&gt;Agents work from declared contracts. When an agent calls a tool via MCP, the only thing it knows about that tool is what the schema says. Tool name, description, parameters, return type. If the schema is wrong, the agent's plan is wrong. There is no Slack channel to check. There is no senior engineer who has seen this bug before. The agent does what the contract advertises, and when reality diverges from the contract, the agent loops, hallucinates, or fails.&lt;/p&gt;

&lt;p&gt;Agents do not pattern-match across services the way developers do. A developer who has used twenty APIs has a strong prior about how errors look, how pagination works, how dates are encoded. They bring that prior to every new API and use it to fill in gaps. An agent has only the current contract. If the contract is incomplete, the gap is a coin flip.&lt;/p&gt;

&lt;p&gt;Agents fail loudly and expensively. A confused developer pauses and asks a question. A confused agent burns tokens, retries, fans out, and produces a plausible-looking wrong answer. Every retry is paid for. Every loop shows up as latency. Every wrong tool call may have side effects the agent does not understand it has triggered.&lt;/p&gt;

&lt;p&gt;Agents are cheap and parallel. There will be more agent traffic against your API in 2027 than human-developer traffic, by orders of magnitude, on any service that gets MCP-exposed. The handful of pain points your developers tolerate gets multiplied by a number you have not budgeted for.&lt;/p&gt;

&lt;p&gt;Concretely, every item in the previous section becomes something different when the caller is an agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spec drift becomes silent tool failure.&lt;/li&gt;
&lt;li&gt;Field shape inconsistency becomes unparseable responses and retry loops.&lt;/li&gt;
&lt;li&gt;Inconsistent error contracts become an agent that does not know whether to back off, retry, or escalate.&lt;/li&gt;
&lt;li&gt;Auth quirks become unpredictable 401s the agent has no strategy for.&lt;/li&gt;
&lt;li&gt;Undocumented side effects become consequences the agent never planned for and cannot reason about.&lt;/li&gt;
&lt;li&gt;Inconsistent pagination becomes either truncated answers or runaway scans.&lt;/li&gt;
&lt;li&gt;Inconsistent rate limit signals become traffic that does not back off, because the agent does not recognise the signal as a limit.&lt;/li&gt;
&lt;li&gt;Missing operation IDs and descriptions become tools the agent never selects, because nothing in the schema told it what they do.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cost of inconsistency stops being a friction tax on developer time and becomes a reliability tax on production.&lt;/p&gt;




&lt;h3&gt;
  
  
  The reframe: agent readiness is API discipline
&lt;/h3&gt;

&lt;p&gt;For a decade, the case for spec-first development, schema linting, consistent error contracts, and centralized auth has been made on developer-experience grounds. Cleaner specs make for happier integrators. Consistent errors make for nicer SDKs. Centralized auth makes for a saner security review. All true, all worth doing, all easy to push to next quarter.&lt;/p&gt;

&lt;p&gt;The agent era reframes the same investments as reliability work. The exact same backlog, with a different price tag attached.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec discipline is no longer about docs.&lt;/strong&gt; Your OpenAPI file is the input to the tool surface agents see. A missing &lt;code&gt;description&lt;/code&gt; is a tool the agent cannot use. A missing &lt;code&gt;operationId&lt;/code&gt; is a tool with no stable identity. A wrong type is a contract the agent will honor and the runtime will reject.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent error contracts are no longer about SDK ergonomics.&lt;/strong&gt; They are the signal agents use to decide between "retry," "back off," "ask the human," and "escalate." Without consistency, every agent has to implement bespoke heuristics per endpoint, and most will get it wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized auth is no longer about security review.&lt;/strong&gt; It is about giving agents one predictable failure mode for "you are not allowed to do this," instead of N endpoint-specific ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits and quotas are no longer about cost control.&lt;/strong&gt; They are the only thing standing between an agent in a loop and your database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability is no longer about debugging.&lt;/strong&gt; It is about being able to answer "what did the agent do last night, in what order, with what consequences," which is a question your team will start getting asked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a new investment. It is the investment you have been postponing, with a new and less negotiable deadline.&lt;/p&gt;




&lt;h3&gt;
  
  
  Where the gateway sits in this
&lt;/h3&gt;

&lt;p&gt;If the contract is the thing agents depend on, the question is where the contract gets enforced. The historical answer, "each backend service enforces its own piece," is exactly the pattern that produced the inconsistencies in the first place. Ten teams will produce ten error envelopes, ten auth flows, and ten rate-limit responses, no matter how good the style guide is.&lt;/p&gt;

&lt;p&gt;The gateway is the natural enforcement point because it is the only place that sees every request the same way. Auth, rate limits, validation, error shape, and observability can be applied uniformly there, without asking ten teams to coordinate. This is the same argument that drove the API gateway category fifteen years ago, with a different forcing function: agents instead of integration partners.&lt;/p&gt;

&lt;p&gt;In practice, agent-readiness at the gateway looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec-first compilation.&lt;/strong&gt; The OpenAPI spec is not documentation that lives next to the gateway; it is the input the gateway is built from. Tool surfaces, request validation, and response schemas are all derived from the same artifact. There is nothing to drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uniform auth and authorization.&lt;/strong&gt; One identity story across every operation, with &lt;a href="https://barbacane.dev/blog/authorization-at-the-gateway/" rel="noopener noreferrer"&gt;policy decisions made at the gateway&lt;/a&gt; before the request reaches a backend that might implement them differently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One error contract.&lt;/strong&gt; Every failure mode, validation, auth, rate limit, upstream error, returns the same shape. Agents learn one envelope, not ten.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent rate limit signaling.&lt;/strong&gt; One &lt;code&gt;429&lt;/code&gt; shape, one &lt;code&gt;Retry-After&lt;/code&gt; semantic, one rate-limit headers convention, applied to every route the gateway fronts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spec validation in CI.&lt;/strong&gt; Missing descriptions, missing operation IDs, drifted types, and inconsistent error references fail the build, not the agent at 3am. Linting the spec used to be a nice-to-have. With agents in the loop, it is the cheapest reliability investment available.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-specific middleware.&lt;/strong&gt; Token-based limits, prompt and response guarding, cost attribution, and per-agent audit logs sit at the same layer as the API governance, not as a separate sidecar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is new infrastructure. It is the API gateway you already wanted, with the dev-experience arguments replaced by reliability arguments, and the deadline moved up.&lt;/p&gt;




&lt;h3&gt;
  
  
  What to do about it
&lt;/h3&gt;

&lt;p&gt;The honest version of the advice: take the list of things your developers have been quietly working around, and treat it as a backlog of agent-readiness work. Sort it by how often the workaround shows up in client code, because that is a good proxy for how often an agent will hit it.&lt;/p&gt;

&lt;p&gt;A few specific moves that pay off quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lint your specs in CI.&lt;/strong&gt; Use Vacuum, Spectral, or equivalent. Fail the build on missing descriptions, missing &lt;code&gt;operationId&lt;/code&gt;s, undeclared error responses, and inconsistent schema references. This is a one-week project that catches a quarter of the problems described above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick one error contract and enforce it at the gateway.&lt;/strong&gt; RFC 9457 problem details is a defensible default. The exact choice matters less than the consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move authorization decisions to the gateway&lt;/strong&gt; where the decision can be expressed declaratively, not implemented per service. CEL for route-level guards, OPA for centralized policy; &lt;a href="https://barbacane.dev/blog/authorization-at-the-gateway/" rel="noopener noreferrer"&gt;we wrote about the two approaches here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit your rate-limit responses.&lt;/strong&gt; Pick one signaling convention. Make sure every limit, gateway-side and backend-side, follows it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat your OpenAPI as the source of truth, not a derivative.&lt;/strong&gt; If your gateway is configured separately from your spec, your gateway will drift from your spec, and your agents will fail in the gap. &lt;a href="https://barbacane.dev/blog/beyond-configuration-drift/" rel="noopener noreferrer"&gt;The compile-don't-configure pattern&lt;/a&gt; closes the gap by construction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide what gets MCP-exposed at the spec level&lt;/strong&gt;, not in a parallel registry. Per-operation opt-out via spec annotation keeps the agent surface honest. &lt;a href="https://barbacane.dev/blog/what-is-an-mcp-gateway/" rel="noopener noreferrer"&gt;More on that here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern across all of these is the same: the contract is the thing agents depend on, the gateway is the place where the contract becomes operational, and the spec is the source of truth that connects them.&lt;/p&gt;




&lt;h3&gt;
  
  
  Closing thoughts
&lt;/h3&gt;

&lt;p&gt;The agent era is not a new layer of work bolted on top of API platforms. It is a forcing function that converts the discretionary improvements API teams have been making the case for, contract discipline, consistency, centralized policy, observable surfaces, into operational requirements. The work was always worth doing. The new thing is that postponing it now produces user-visible failures instead of developer-visible friction.&lt;/p&gt;

&lt;p&gt;The good news is that everything that makes an API agent-ready also makes it better for the humans who were already using it. The teams that get this right do not end up with a second platform for agents. They end up with one well-governed platform that happens to also be safe for agents to call.&lt;/p&gt;

&lt;p&gt;At Barbacane we build that platform on the assumption that this is where things are going: spec-first compilation, gateway-enforced governance, MCP exposure derived from the same OpenAPI you already maintain. The categories matter more than the product. But the categories are converging quickly, and the teams that act on the convergence early will skip the second-system rebuild that the ones who delay are about to start.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Barbacane is open source (AGPLv3) and available at &lt;a href="https://github.com/barbacane-dev/barbacane" rel="noopener noreferrer"&gt;github.com/barbacane-dev/barbacane&lt;/a&gt;. If MCP and agent-readiness are on your roadmap, &lt;a href="https://barbacane.dev/mcp/" rel="noopener noreferrer"&gt;the /mcp page&lt;/a&gt; is the short version of how we approach it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aiagents</category>
      <category>mcp</category>
      <category>mcpgateway</category>
      <category>apigovernance</category>
    </item>
    <item>
      <title>Authorization at the gateway: CEL and OPA for policy-driven access control</title>
      <dc:creator>Nico</dc:creator>
      <pubDate>Thu, 07 May 2026 08:01:24 +0000</pubDate>
      <link>https://dev.to/ndreno/authorization-at-the-gateway-cel-and-opa-for-policy-driven-access-control-2afl</link>
      <guid>https://dev.to/ndreno/authorization-at-the-gateway-cel-and-opa-for-policy-driven-access-control-2afl</guid>
      <description>&lt;p&gt;&lt;em&gt;Authentication is a solved problem. Authorization is where things get complicated.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you know &lt;em&gt;who&lt;/em&gt; is making a request, how do you decide &lt;em&gt;what they're allowed to do&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;At small scale, authorization is simple. An &lt;code&gt;admin&lt;/code&gt; role gets full access, a &lt;code&gt;viewer&lt;/code&gt; role gets read-only. You hardcode a few rules and move on. But enterprise APIs don't stay small. Teams multiply, services proliferate, and authorization logic becomes a tangled web of role hierarchies, resource ownership, temporal constraints, and regulatory requirements.&lt;/p&gt;

&lt;p&gt;This is where most gateway setups start to crack.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Authorization Gap
&lt;/h3&gt;

&lt;p&gt;Traditional API gateways handle authentication well. JWT validation, API key checks, OAuth2 introspection: these are table stakes. But once the token is verified, the authorization question is typically punted to the application layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gateway: "This is Alice, she has a valid token."
Backend: "Great, but can Alice delete this specific order?"
Gateway: "¯\_(ツ)_/¯"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pushes authorization logic into every backend service. Each team implements its own checks. Rules diverge. Auditing becomes a nightmare. And when a policy change is needed, say, revoking access for a departing employee's role, you're patching multiple services instead of updating one policy.&lt;/p&gt;

&lt;p&gt;The alternative is moving authorization decisions to the gateway, where they can be enforced &lt;em&gt;before&lt;/em&gt; the request reaches your backends. But this requires expressive policy languages, not just role lists.&lt;/p&gt;




&lt;h3&gt;
  
  
  Two Philosophies, One Gateway
&lt;/h3&gt;

&lt;p&gt;Barbacane now ships two authorization plugins that represent fundamentally different approaches to the same problem:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;CEL&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;OPA&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inline, in-process&lt;/td&gt;
&lt;td&gt;External service (HTTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CEL expressions&lt;/td&gt;
&lt;td&gt;Rego policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microseconds&lt;/td&gt;
&lt;td&gt;HTTP round-trip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Policy location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In the OpenAPI spec&lt;/td&gt;
&lt;td&gt;In a policy repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Route-level guards&lt;/td&gt;
&lt;td&gt;Centralized policy management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They're not competing. They're complementary. Most enterprise deployments will use both.&lt;/p&gt;




&lt;h3&gt;
  
  
  CEL: Inline Policy Expressions
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://cel.dev/" rel="noopener noreferrer"&gt;CEL (Common Expression Language)&lt;/a&gt; is a lightweight expression language designed by Google for evaluating policies. It's the same language behind Kubernetes admission webhooks, Envoy RBAC filters, and Firebase Security Rules. If you've written a CEL expression anywhere in the cloud-native ecosystem, you already know how it works in Barbacane.&lt;/p&gt;

&lt;p&gt;The CEL plugin evaluates expressions directly in the gateway process. No sidecar. No HTTP call. No external dependency. You write the rule in your spec, and it runs at request time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/admin/users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;x-barbacane-middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jwt-auth&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://auth.example.com"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cel&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;'admin'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;request.claims.roles"&lt;/span&gt;
            &lt;span class="na"&gt;deny_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Admin&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;access&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expression has access to the full request context: method, path, headers, query parameters, client IP, and (critically) the parsed claims from an upstream auth middleware:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Only admins can DELETE
request.method != 'DELETE' || 'admin' in request.claims.roles

// Rate-limit bypass for internal services
request.headers['x-internal-service'] != '' &amp;amp;&amp;amp; request.client_ip.startsWith('10.')

// Time-based access (with string comparison on ISO timestamps)
request.method == 'GET' || request.claims.role == 'admin'

// Resource ownership via path params
request.claims.sub == request.path_params.user_id || 'admin' in request.claims.roles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because CEL evaluates in-process, latency overhead is measured in microseconds. There's no network hop, no serialization, no external service to monitor. The expression is compiled once and cached for subsequent requests.&lt;/p&gt;

&lt;p&gt;This makes CEL ideal for &lt;strong&gt;route-level guards&lt;/strong&gt;: rules that are specific to an endpoint and belong alongside the route definition. When you read the spec, you see exactly what's enforced. The policy &lt;em&gt;is&lt;/em&gt; the configuration, the same principle that drives everything in Barbacane.&lt;/p&gt;




&lt;h3&gt;
  
  
  OPA: Centralized Policy Engine
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.openpolicyagent.org/" rel="noopener noreferrer"&gt;Open Policy Agent&lt;/a&gt; takes the opposite approach: policies live outside your specs, in a dedicated policy repository, written in Rego (OPA's purpose-built policy language). The gateway sends request context to OPA via its REST API and enforces the boolean decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/orders/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;x-barbacane-middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oauth2-auth&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;introspection_endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://auth.example.com/introspect"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opa-authz&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;opa_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://opa:8181/v1/data/api/authz/allow"&lt;/span&gt;
            &lt;span class="na"&gt;include_claims&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OPA plugin constructs an input payload from the request and POSTs it to your OPA endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DELETE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders/ord-42"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"x-auth-consumer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"client_ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"claims"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"order-manager"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"department"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fulfillment"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Rego policy evaluates the decision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rego"&gt;&lt;code&gt;&lt;span class="ow"&gt;package&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authz&lt;/span&gt;

&lt;span class="ow"&gt;default&lt;/span&gt; &lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="c1"&gt;# Order managers can delete orders in their department&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"DELETE"&lt;/span&gt;
    &lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/orders/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"order-manager"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Admins can do anything&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"admin"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Read-only access for authenticated users&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="n"&gt;if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sub&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This model introduces an HTTP round-trip per request, which is a real cost. But what you get in return is significant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized policy management.&lt;/strong&gt; All authorization rules live in one repository, versioned and reviewed like code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupled policy evolution.&lt;/strong&gt; Update policies without recompiling gateway artifacts or redeploying services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit trails.&lt;/strong&gt; OPA's decision logs provide a complete record of every authorization decision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex logic.&lt;/strong&gt; Rego supports data joins, partial evaluation, and recursive rules that go well beyond what inline expressions can express.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For organizations that need to answer "who had access to what, and when?", think compliance-heavy industries, multi-tenant platforms, regulated APIs, OPA is the right tool.&lt;/p&gt;




&lt;h3&gt;
  
  
  Composing Authorization with Authentication
&lt;/h3&gt;

&lt;p&gt;Both plugins are designed to slot into Barbacane's middleware chain after an authentication middleware. The auth plugin sets standard headers (&lt;code&gt;x-auth-consumer&lt;/code&gt;, &lt;code&gt;x-auth-consumer-groups&lt;/code&gt;, &lt;code&gt;x-auth-claims&lt;/code&gt;) that the authorization plugin reads. This decoupling means you can swap auth methods without touching authorization logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Global: authenticate with OIDC&lt;/span&gt;
&lt;span class="na"&gt;x-barbacane-middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oidc-auth&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;issuer_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://accounts.google.com"&lt;/span&gt;
      &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-api"&lt;/span&gt;

&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/admin/settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Route-level: CEL guard for admin-only&lt;/span&gt;
      &lt;span class="na"&gt;x-barbacane-middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cel&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;'admin'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;request.claims.roles"&lt;/span&gt;

  &lt;span class="s"&gt;/reports/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Route-level: OPA for complex ownership rules&lt;/span&gt;
      &lt;span class="na"&gt;x-barbacane-middlewares&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opa-authz&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;opa_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://opa:8181/v1/data/reports/access"&lt;/span&gt;
            &lt;span class="na"&gt;include_claims&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what's happening: OIDC authentication is global, but authorization varies per route. Simple admin checks use CEL (no external dependency, microsecond overhead). Complex ownership checks delegate to OPA (centralized policy, full audit trail). The gateway runs the right tool for each endpoint.&lt;/p&gt;

&lt;p&gt;This layered approach also composes with Barbacane's existing &lt;a href="https://barbacane.dev/blog/beyond-configuration-drift/" rel="noopener noreferrer"&gt;ACL middleware&lt;/a&gt;, which handles group-based allow/deny lists. For many routes, ACL is sufficient. CEL and OPA extend the authorization spectrum for cases where group membership alone isn't enough.&lt;/p&gt;




&lt;h3&gt;
  
  
  Choosing the Right Tool
&lt;/h3&gt;

&lt;p&gt;Here's a practical decision framework:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use ACL when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization is based on group/role membership (e.g., "admins can access /admin/*")&lt;/li&gt;
&lt;li&gt;Rules are static allow/deny lists&lt;/li&gt;
&lt;li&gt;You don't need expression logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use CEL when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rules are specific to a route and benefit from living in the spec&lt;/li&gt;
&lt;li&gt;You need expressions beyond simple group checks (method + path + claims combinations)&lt;/li&gt;
&lt;li&gt;Latency is critical (no external dependency)&lt;/li&gt;
&lt;li&gt;The team maintaining the spec also owns the authorization rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use OPA when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Policies are managed by a dedicated security/platform team&lt;/li&gt;
&lt;li&gt;Rules are complex, cross-cutting, or frequently updated independently of deployments&lt;/li&gt;
&lt;li&gt;You need audit logs of every authorization decision&lt;/li&gt;
&lt;li&gt;Compliance requirements mandate centralized policy governance&lt;/li&gt;
&lt;li&gt;Policies reference external data (user attributes, resource metadata)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use CEL + OPA together when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple route guards in CEL, complex cross-cutting policies in OPA&lt;/li&gt;
&lt;li&gt;CEL as a fast pre-filter, OPA for the authoritative decision&lt;/li&gt;
&lt;li&gt;Different teams own different parts of the authorization surface&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Toward Zero Trust at the Gateway
&lt;/h3&gt;

&lt;p&gt;The traditional enterprise pattern, check roles in a middleware, check permissions in the backend, hope they agree, is fundamentally at odds with zero trust. In a zero trust model, no request is trusted by default. Every call, whether it comes from the public internet or from a service two hops away in your Kubernetes cluster, must be explicitly verified against policy before it reaches its destination.&lt;/p&gt;

&lt;p&gt;The API gateway is a natural enforcement point for this. It already sits on the request path. It already knows the caller's identity (via auth middleware). What's been missing is the ability to express and evaluate &lt;em&gt;policies&lt;/em&gt;, not just check role lists.&lt;/p&gt;

&lt;p&gt;That's what CEL and OPA bring to the table. Every request gets evaluated against an explicit policy. Internal traffic doesn't get a free pass. External traffic doesn't get a different code path. The same expressions, the same Rego rules, the same decision framework applies everywhere. And because policies are declared in the spec (CEL) or in a versioned policy repo (OPA), they're auditable. You can answer "what policy was enforced on this endpoint last Tuesday?" without digging through application logs.&lt;/p&gt;

&lt;p&gt;This doesn't mean moving &lt;em&gt;all&lt;/em&gt; authorization to the gateway. Fine-grained object-level checks ("can Alice edit &lt;em&gt;this specific document&lt;/em&gt;?") still belong in the backend, where you have the data context. But coarse-grained and medium-grained decisions ("can this role call DELETE on this endpoint?", "does this department have access to this API?", "is this consumer allowed to write to production resources?") are gateway concerns. Enforcing them before the request reaches your backend reduces attack surface, simplifies backend code, and provides a single enforcement point that security teams can actually audit.&lt;/p&gt;

&lt;p&gt;With CEL and OPA, Barbacane gives you two industry-standard tools for building this layer. CEL for the rules that belong next to the route definition. OPA for the policies that belong in a dedicated repository. Both enforced at the same gateway, both integrated with the same authentication chain, both verifiable before deployment.&lt;/p&gt;




&lt;h3&gt;
  
  
  Strengths and Tradeoffs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What works well:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CEL expressions are validated at request time, catching typos early&lt;/li&gt;
&lt;li&gt;OPA integration uses the standard Data API, so any OPA deployment works&lt;/li&gt;
&lt;li&gt;Both plugins produce RFC 9457 Problem Details for HTTP APIs, consistent with the rest of the gateway&lt;/li&gt;
&lt;li&gt;Authentication and authorization are cleanly separated via standard headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to keep in mind:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CEL expressions live in your spec, so changes require recompilation and redeployment&lt;/li&gt;
&lt;li&gt;OPA adds an HTTP round-trip per request (mitigated by running OPA as a local sidecar)&lt;/li&gt;
&lt;li&gt;The OPA plugin evaluates a single boolean decision; structured deny reasons require custom Rego&lt;/li&gt;
&lt;li&gt;CEL doesn't support external data lookups; if your policy needs database queries, use OPA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are deliberate design choices. CEL optimizes for speed and spec-locality. OPA optimizes for flexibility and centralized governance. Barbacane gives you both, and the middleware chain lets you compose them per route.&lt;/p&gt;




&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Both plugins are available today. Add them to your spec, compile, and deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# CEL: no external dependencies&lt;/span&gt;
barbacane compile &lt;span class="nt"&gt;--spec&lt;/span&gt; api.yaml &lt;span class="nt"&gt;-m&lt;/span&gt; barbacane.yaml &lt;span class="nt"&gt;-o&lt;/span&gt; api.bca

&lt;span class="c"&gt;# OPA: run OPA alongside your gateway&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8181:8181 openpolicyagent/opa:latest run &lt;span class="nt"&gt;--server&lt;/span&gt; /policies
barbacane compile &lt;span class="nt"&gt;--spec&lt;/span&gt; api.yaml &lt;span class="nt"&gt;-m&lt;/span&gt; barbacane.yaml &lt;span class="nt"&gt;-o&lt;/span&gt; api.bca
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.barbacane.dev/guide/middlewares.html" rel="noopener noreferrer"&gt;middleware documentation&lt;/a&gt; covers configuration details, expression syntax, and OPA input format. The &lt;a href="https://docs.barbacane.dev/contributing/plugins.html" rel="noopener noreferrer"&gt;plugin development guide&lt;/a&gt; shows how to build custom authorization plugins if CEL and OPA don't fit your model.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Barbacane is open source (AGPLv3) and available at &lt;a href="https://github.com/barbacane-dev/barbacane" rel="noopener noreferrer"&gt;github.com/barbacane-dev/barbacane&lt;/a&gt;. The CEL and OPA authorization plugins ship with v0.1.x. Try them against your specs and let us know what works.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>barbacane</category>
      <category>apigateway</category>
      <category>authorization</category>
      <category>cel</category>
    </item>
    <item>
      <title>One gateway, many specs: how Barbacane unifies your API ecosystem</title>
      <dc:creator>Nico</dc:creator>
      <pubDate>Tue, 05 May 2026 08:04:12 +0000</pubDate>
      <link>https://dev.to/ndreno/one-gateway-many-specs-how-barbacane-unifies-your-api-ecosystem-1gg8</link>
      <guid>https://dev.to/ndreno/one-gateway-many-specs-how-barbacane-unifies-your-api-ecosystem-1gg8</guid>
      <description>&lt;p&gt;&lt;em&gt;Most API tooling assumes one repository, one specification. But your architecture doesn't work that way.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://barbacane.dev/blog/beyond-configuration-drift/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, we explored how Barbacane eliminates configuration drift by compiling your OpenAPI spec directly into the gateway's runtime artifact. One spec, one &lt;code&gt;.bca&lt;/code&gt; file, zero drift.&lt;/p&gt;

&lt;p&gt;But what happens when your architecture has more than one spec?&lt;/p&gt;




&lt;h3&gt;
  
  
  The Multi-Spec Reality
&lt;/h3&gt;

&lt;p&gt;In a typical microservices setup, your API surface isn't described by a single file. Whether you're working contract-first or generating specs from code, you end up with multiple specifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;User Service&lt;/strong&gt; with its own &lt;code&gt;openapi.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Order Service&lt;/strong&gt; with its own &lt;code&gt;openapi.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Inventory Service&lt;/strong&gt; exposing both REST endpoints and event consumers, described across OpenAPI and AsyncAPI files&lt;/li&gt;
&lt;li&gt;Event schemas scattered across repos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each file is validated in isolation, deployed independently, and versioned on its own timeline. Single-spec tools can't see across these boundaries, so cross-service mismatches only surface at runtime. The feedback loop is as slow as it gets: write specs, deploy, discover the conflict in production, fix, redeploy.&lt;/p&gt;




&lt;h3&gt;
  
  
  One Command, Multiple Specs
&lt;/h3&gt;

&lt;p&gt;Barbacane's &lt;code&gt;compile&lt;/code&gt; command accepts multiple specification files in a single invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;barbacane compile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/user-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/order-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/inventory-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/inventory-service/asyncapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; barbacane.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; gateway.bca
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler parses every file (OpenAPI 3.x and AsyncAPI 3.0.x) and merges their routes into a single &lt;code&gt;.bca&lt;/code&gt; artifact. The output message tells you exactly what you got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;compiled 4 spec(s) to gateway.bca (23 routes, 5 plugin(s) bundled)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One artifact. One routing table. One deployment.&lt;/p&gt;




&lt;h3&gt;
  
  
  What Multi-Spec Compilation Actually Does
&lt;/h3&gt;

&lt;p&gt;When you pass multiple specs, the compiler:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parses each file independently.&lt;/strong&gt; OpenAPI and AsyncAPI specs are each validated against their respective standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Merges routes into a unified routing table.&lt;/strong&gt; All operations from all specs end up in a single &lt;code&gt;routes.json&lt;/code&gt; inside the &lt;code&gt;.bca&lt;/code&gt; artifact. The gateway doesn't care which file a route came from; it serves them all.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Detects routing conflicts.&lt;/strong&gt; If two specs define the same path and method combination (e.g., both declare &lt;code&gt;GET /users/{id}&lt;/code&gt;), compilation fails with error &lt;code&gt;E1010&lt;/code&gt;. This is a hard gate: you cannot produce an artifact with ambiguous routing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bundles everything together.&lt;/strong&gt; WASM plugins, dispatcher configurations, and the original source specs are all packaged into the artifact. The source specs remain accessible at &lt;code&gt;/__barbacane/specs&lt;/code&gt; for documentation and debugging.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn't magic. It's the same compilation pipeline applied across multiple input files. But the practical impact is a meaningful &lt;strong&gt;shift left&lt;/strong&gt;: routing conflicts that would previously surface as mysterious 404s or wrong-handler bugs in production now fail your build.&lt;/p&gt;




&lt;h3&gt;
  
  
  Specs Stay Accessible at Runtime
&lt;/h3&gt;

&lt;p&gt;Compilation merges routes, but the original source specs aren't thrown away. They're embedded in the &lt;code&gt;.bca&lt;/code&gt; artifact and served by the gateway at &lt;code&gt;/__barbacane/specs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /__barbacane/specs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns an index of every spec that was compiled into the running artifact, with links to the full OpenAPI and AsyncAPI documents. The served specs are stripped of Barbacane-specific extensions (&lt;code&gt;x-barbacane-*&lt;/code&gt;), so what your API consumers and documentation tools see is clean, standard OpenAPI and AsyncAPI with no vendor-specific noise. And because these are the exact specs that were compiled, they can't drift from what the gateway is actually running.&lt;/p&gt;

&lt;p&gt;No separate spec hosting. No stale docs. The gateway &lt;em&gt;is&lt;/em&gt; the documentation server.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Practical Example
&lt;/h3&gt;

&lt;p&gt;Consider an e-commerce platform with four services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Service      → openapi.yaml
Order Service     → openapi.yaml
Inventory Service → openapi.yaml + asyncapi.yaml
Notification Svc  → asyncapi.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without multi-spec compilation, you'd deploy each service's gateway configuration independently, trusting that teams coordinated their path prefixes and schema versions. With Barbacane:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;barbacane compile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/user-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/order-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/inventory-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/inventory-service/asyncapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; services/notification-service/asyncapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; barbacane.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; gateway.bca
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the Order Service accidentally defines a route that collides with the User Service, compilation fails. You find out in seconds, not after a deploy.&lt;/p&gt;




&lt;h3&gt;
  
  
  CI/CD Integration
&lt;/h3&gt;

&lt;p&gt;Multi-spec compilation fits naturally into a CI gate. Block merges to &lt;code&gt;main&lt;/code&gt; if the combined specs don't compile cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/validate-contracts.yml&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Compile gateway artifact&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;barbacane compile \&lt;/span&gt;
            &lt;span class="s"&gt;-s services/user-service/openapi.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;-s services/order-service/openapi.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;-s services/inventory-service/openapi.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;-s services/inventory-service/asyncapi.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;-m barbacane.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;-o gateway.bca&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A non-zero exit code (1 for validation failures, 2 for manifest errors) blocks the pipeline. No ambiguous warnings: either the specs compile together, or they don't.&lt;/p&gt;




&lt;h3&gt;
  
  
  Progressive Adoption
&lt;/h3&gt;

&lt;p&gt;You don't have to compile everything at once. Start with your most critical services and expand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start with two services&lt;/span&gt;
barbacane compile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; user-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; auth-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; barbacane.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; gateway.bca

&lt;span class="c"&gt;# Later, add event-driven services&lt;/span&gt;
barbacane compile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; user-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; auth-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; order-service/openapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-s&lt;/span&gt; order-service/asyncapi.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; barbacane.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; gateway.bca
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each additional spec increases the surface area of conflict detection. The more specs you compile together, the more mismatches you catch before deployment.&lt;/p&gt;




&lt;h3&gt;
  
  
  Strengths and Limitations
&lt;/h3&gt;

&lt;p&gt;Multi-spec compilation extends the "compile, don't configure" philosophy across service boundaries, but it's worth understanding what it does and doesn't do today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it catches:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing conflicts (duplicate path + method across specs)&lt;/li&gt;
&lt;li&gt;Spec-level validation errors (malformed OpenAPI/AsyncAPI)&lt;/li&gt;
&lt;li&gt;Missing plugin or dispatcher declarations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What it doesn't do (yet):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-spec schema validation (e.g., verifying that an &lt;code&gt;Order&lt;/code&gt; object is consistent between two specs)&lt;/li&gt;
&lt;li&gt;Breaking change detection between spec versions&lt;/li&gt;
&lt;li&gt;Dependency graph analysis between services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are real limitations. Multi-spec compilation today is primarily about &lt;em&gt;route-level unification and conflict detection&lt;/em&gt;, not deep semantic analysis across your API ecosystem. For schema consistency, you'll still need complementary tooling or careful code review.&lt;/p&gt;




&lt;h3&gt;
  
  
  Shifting Left Across Service Boundaries
&lt;/h3&gt;

&lt;p&gt;The idea behind "shift left" is simple: catch problems earlier in the development lifecycle, when they're cheapest to fix. Linters shift left on code quality. Type systems shift left on correctness. Multi-spec compilation shifts left on &lt;em&gt;cross-service integration&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://barbacane.dev/blog/beyond-configuration-drift/" rel="noopener noreferrer"&gt;previous article&lt;/a&gt;, we showed how Barbacane shifts gateway configuration left by compiling the spec into the runtime artifact. Multi-spec compilation takes this further: instead of discovering that two services disagree on routing after deployment, you discover it at compile time, in CI, on a pull request.&lt;/p&gt;

&lt;p&gt;It's not a silver bullet. Cross-service consistency is a hard problem, and route-level conflict detection is just one piece of the puzzle. But it's a piece that most gateway tooling doesn't offer at all, and one that pays off immediately in any multi-service architecture. The earlier you catch a conflict, the less it costs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Barbacane is open source and available at &lt;a href="https://github.com/barbacane-dev/barbacane" rel="noopener noreferrer"&gt;github.com/barbacane-dev/barbacane&lt;/a&gt;. Check the &lt;a href="https://docs.barbacane.dev/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for the full CLI reference and getting started guide.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>barbacane</category>
      <category>apigateway</category>
      <category>openapi</category>
      <category>asyncapi</category>
    </item>
    <item>
      <title>Beyond configuration drift: how Barbacane reimagines the API gateway with Rust and WASM</title>
      <dc:creator>Nico</dc:creator>
      <pubDate>Wed, 29 Apr 2026 18:54:22 +0000</pubDate>
      <link>https://dev.to/ndreno/beyond-configuration-drift-how-barbacane-reimagines-the-api-gateway-with-rust-and-wasm-god</link>
      <guid>https://dev.to/ndreno/beyond-configuration-drift-how-barbacane-reimagines-the-api-gateway-with-rust-and-wasm-god</guid>
      <description>&lt;p&gt;&lt;em&gt;What if your OpenAPI spec wasn't just documentation, but the actual configuration of your production gateway?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For years, API teams have lived with a quiet frustration: the gap between specification and reality. You write a beautiful OpenAPI spec. You configure your gateway (Kong, Tyk, AWS API Gateway) with routes, plugins, and security rules. And then… drift happens. A hotfix bypasses the spec. A plugin gets misconfigured. The documentation lies. The gateway behaves unexpectedly. The contract between frontend and backend fractures.&lt;/p&gt;

&lt;p&gt;This isn't a people problem. It's an architecture problem.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;Barbacane&lt;/strong&gt;, a spec-driven API gateway built in Rust that treats your OpenAPI (and AsyncAPI) specification as the &lt;em&gt;single source of truth&lt;/em&gt;. No separate configuration files. No UI clicks that diverge from Git. Just your spec, compiled into a self-contained artifact that runs at the edge with memory safety guarantees and sub-millisecond latency.&lt;/p&gt;

&lt;p&gt;Let's dive into why this approach matters, and whether it's ready for your production workloads.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Configuration Drift Crisis
&lt;/h3&gt;

&lt;p&gt;Most API gateways follow the same pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You write an OpenAPI spec (hopefully)&lt;/li&gt;
&lt;li&gt;You &lt;em&gt;separately&lt;/em&gt; configure the gateway via YAML, UI, or CLI&lt;/li&gt;
&lt;li&gt;You hope these two artifacts stay in sync&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This dual-source model creates inevitable drift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# openapi.yaml&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/users/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[{&lt;/span&gt; &lt;span class="nv"&gt;jwt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt; &lt;span class="pi"&gt;}]&lt;/span&gt;

&lt;span class="c1"&gt;# kong.yaml (oops, forgot to add auth plugin!)&lt;/span&gt;
&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;users-get&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;/users/&lt;/span&gt;&lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;}]&lt;/span&gt;
    &lt;span class="c1"&gt;# missing jwt-auth plugin configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result? A route that &lt;em&gt;should&lt;/em&gt; require authentication ships to production wide open. Security teams panic. Post-mortems happen. Trust erodes.&lt;/p&gt;

&lt;p&gt;Barbacane eliminates this entire class of failure by making drift &lt;em&gt;architecturally impossible&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Core Insight: Compile, Don't Configure
&lt;/h3&gt;

&lt;p&gt;Barbacane's philosophy is radical in its simplicity:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Your spec is your gateway.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of parsing specs at runtime or maintaining parallel configuration, Barbacane introduces a &lt;em&gt;compilation step&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 1: Write your spec (as usual)&lt;/span&gt;
openapi: 3.1.0
info:
  title: User API
  version: 1.0.0
x-barbacane-plugins:
  - name: oidc-auth
    config:
      issuer_url: &lt;span class="s2"&gt;"https://auth.example.com"&lt;/span&gt;
      audience: &lt;span class="s2"&gt;"my-api"&lt;/span&gt;

&lt;span class="c"&gt;# Step 2: Compile it&lt;/span&gt;
barbacane compile &lt;span class="nt"&gt;--spec&lt;/span&gt; openapi.yaml &lt;span class="nt"&gt;--manifest&lt;/span&gt; barbacane.yaml &lt;span class="nt"&gt;--output&lt;/span&gt; api.bca

&lt;span class="c"&gt;# Step 3: Run the gateway&lt;/span&gt;
barbacane serve &lt;span class="nt"&gt;--artifact&lt;/span&gt; api.bca
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.bca&lt;/code&gt; artifact is a self-contained binary bundle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-compiled routing trie (FlatBuffers, zero-copy deserialization)&lt;/li&gt;
&lt;li&gt;JSON Schema validators for request/response validation&lt;/li&gt;
&lt;li&gt;WASM plugins (including your auth middleware)&lt;/li&gt;
&lt;li&gt;OPA policies for fine-grained authorization&lt;/li&gt;
&lt;li&gt;Dispatcher configurations (HTTP upstreams, Lambda, Kafka)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Critically: &lt;strong&gt;no runtime spec parsing&lt;/strong&gt;. The gateway starts in &amp;lt;100ms because everything is pre-optimized. What you compile is exactly what runs. No surprises.&lt;/p&gt;




&lt;h3&gt;
  
  
  Architecture Deep Dive: Control Plane vs. Data Plane
&lt;/h3&gt;

&lt;p&gt;Barbacane cleanly separates concerns:&lt;/p&gt;

&lt;h4&gt;
  
  
  The Control Plane (&lt;code&gt;barbacane-control&lt;/code&gt;)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Stateful service (PostgreSQL-backed)&lt;/li&gt;
&lt;li&gt;Handles spec ingestion, validation, and compilation&lt;/li&gt;
&lt;li&gt;Serves artifacts to data planes&lt;/li&gt;
&lt;li&gt;Provides UI for fleet visibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Data Plane (&lt;code&gt;barbacane&lt;/code&gt;)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Completely stateless&lt;/strong&gt; single binary&lt;/li&gt;
&lt;li&gt;Loads &lt;code&gt;.bca&lt;/code&gt; artifact at startup (memory-mapped via FlatBuffers)&lt;/li&gt;
&lt;li&gt;Zero runtime dependencies&lt;/li&gt;
&lt;li&gt;Optional WebSocket connection to control plane for health reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation enables true edge deployment: ship a 15MB static binary with your compiled artifact to a CDN POP, and it runs independently. No coordination required. Scale horizontally by launching more binaries. No consensus protocols. No distributed state.&lt;/p&gt;




&lt;h3&gt;
  
  
  WASM Plugins: Safety Without Sacrifice
&lt;/h3&gt;

&lt;p&gt;Barbacane ships as a "bare binary" with &lt;strong&gt;zero bundled plugins&lt;/strong&gt;. Every capability (JWT auth, rate limiting, CORS) is implemented as a WASM module explicitly declared in your spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;x-barbacane-plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rate-limit&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;quota&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;partition_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;header:x-api-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During compilation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Plugin is fetched from registry (or local cache)&lt;/li&gt;
&lt;li&gt;Validated against spec requirements&lt;/li&gt;
&lt;li&gt;Bundled into the &lt;code&gt;.bca&lt;/code&gt; artifact&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plugins execute in a &lt;code&gt;wasmtime&lt;/code&gt; sandbox with strict resource limits&lt;/li&gt;
&lt;li&gt;Memory isolation prevents plugin crashes from taking down the gateway&lt;/li&gt;
&lt;li&gt;Host functions are capability-gated (e.g., vault access requires explicit grant)&lt;/li&gt;
&lt;li&gt;Execution timeouts prevent CPU starvation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This model delivers what Lua plugins in Kong &lt;em&gt;wish&lt;/em&gt; they had: true isolation without sacrificing performance. Benchmarks show 261us overhead per WASM middleware invocation, including instantiation, on modern hardware.&lt;/p&gt;




&lt;h3&gt;
  
  
  Security by Construction
&lt;/h3&gt;

&lt;p&gt;Barbacane's security model is defense-in-depth by design:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Safety&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rust + WASM sandbox&lt;/td&gt;
&lt;td&gt;Eliminates entire classes of CVEs (buffer overflows, use-after-free)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secrets Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vault fetch at startup only&lt;/td&gt;
&lt;td&gt;No secrets in Git, specs, or artifacts. Only in runtime memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AuthN/AuthZ&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Plugin-based + OPA&lt;/td&gt;
&lt;td&gt;No vendor lock-in; policies compiled to WASM for speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compilation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fail-fast validation&lt;/td&gt;
&lt;td&gt;Blocks dangerous configs early (e.g., &lt;code&gt;http://&lt;/code&gt; backends in prod)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transport&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rustls (no OpenSSL)&lt;/td&gt;
&lt;td&gt;Memory-safe TLS with modern crypto defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For secrets, specs reference them by ID only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;x-barbacane-dispatcher&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http-upstream&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://backend.example.com"&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;vault://prod/api-gateway/backend-token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At startup, the data plane fetches secrets from a secret manager, never storing them on disk. Rotate keys in your secret manager, and the gateway picks up new values on next restart (or via periodic refresh).&lt;/p&gt;




&lt;h3&gt;
  
  
  Performance: Why FlatBuffers Matters
&lt;/h3&gt;

&lt;p&gt;Most gateways deserialize JSON configs at startup. For small specs, this is fine. For large specs (500+ routes, complex schemas), it becomes a bottleneck.&lt;/p&gt;

&lt;p&gt;Barbacane uses &lt;strong&gt;FlatBuffers&lt;/strong&gt; for its artifact format, a choice that pays dividends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-copy deserialization&lt;/strong&gt;: Memory-map the artifact and access data directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup in &amp;lt;100ms&lt;/strong&gt;: Even for 1,000-route specs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No GC pressure&lt;/strong&gt;: Critical for latency-sensitive edge workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema evolution&lt;/strong&gt;: Backward/forward compatibility built-in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benchmarks show route lookup in &lt;strong&gt;83 nanoseconds&lt;/strong&gt; for 1,000 routes, faster than a single L3 cache miss. Full request validation (parameters + body schema) averages &lt;strong&gt;1.2 microseconds&lt;/strong&gt;. This isn't theoretical; it's the difference between viable and non-viable edge deployment.&lt;/p&gt;




&lt;h3&gt;
  
  
  Strengths and Tradeoffs
&lt;/h3&gt;

&lt;p&gt;No tool is the right fit for every situation. Here's where Barbacane shines and what to keep in mind.&lt;/p&gt;

&lt;h4&gt;
  
  
  Strengths
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec integrity&lt;/strong&gt;: Drift is architecturally impossible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security posture&lt;/strong&gt;: Rust + WASM sandboxing beats Lua/JS runtimes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge readiness&lt;/strong&gt;: Stateless, fast startup, minimal footprint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AsyncAPI support&lt;/strong&gt;: Rare among gateways. Handles WebSockets/MQTT alongside HTTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitOps native&lt;/strong&gt;: Specs in Git → CI validation → artifact deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Tradeoffs to consider
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Young project&lt;/strong&gt;: v0.1.x, actively developed with a growing community&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focused plugin set&lt;/strong&gt;: ~17 official plugins covering core use cases, with more on the way&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compile-first workflow&lt;/strong&gt;: Changes go through CI/CD rather than runtime hot-patching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static backends&lt;/strong&gt;: Service discovery requires a custom plugin or DNS-based resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Barbacane prioritizes configuration integrity and safety over plugin breadth and dynamic reconfiguration. If that tradeoff works for your team, it's worth evaluating.&lt;/p&gt;




&lt;h3&gt;
  
  
  Competitive Landscape
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gateway&lt;/th&gt;
&lt;th&gt;Spec-Driven&lt;/th&gt;
&lt;th&gt;Memory Safe&lt;/th&gt;
&lt;th&gt;WASM Plugins&lt;/th&gt;
&lt;th&gt;Edge-Ready&lt;/th&gt;
&lt;th&gt;AsyncAPI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Barbacane&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;First-class&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kong&lt;/td&gt;
&lt;td&gt;Separate config&lt;/td&gt;
&lt;td&gt;Lua/Nginx&lt;/td&gt;
&lt;td&gt;Experimental&lt;/td&gt;
&lt;td&gt;Heavy&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tyk&lt;/td&gt;
&lt;td&gt;Separate config&lt;/td&gt;
&lt;td&gt;Go (GC)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Heavy&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS API Gateway&lt;/td&gt;
&lt;td&gt;Import only&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Managed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KrakenD&lt;/td&gt;
&lt;td&gt;JSON config&lt;/td&gt;
&lt;td&gt;Go (GC)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Barbacane targets a different design point than Kong or Tyk: &lt;em&gt;configuration integrity&lt;/em&gt; and &lt;em&gt;security&lt;/em&gt; over plugin ecosystem breadth.&lt;/p&gt;




&lt;h3&gt;
  
  
  Who Should Consider Barbacane Today?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Strong fits&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Greenfield APIs with OpenAPI-first development workflows&lt;/li&gt;
&lt;li&gt;Edge deployments requiring sub-5ms latency overhead&lt;/li&gt;
&lt;li&gt;Security-sensitive domains (fintech, healthcare, govtech)&lt;/li&gt;
&lt;li&gt;Teams with mature GitOps/CI-CD practices&lt;/li&gt;
&lt;li&gt;Organizations investing in Rust/WASM toolchains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Poor fits&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Legacy systems requiring dynamic runtime reconfiguration&lt;/li&gt;
&lt;li&gt;Teams needing 50+ pre-built plugins immediately&lt;/li&gt;
&lt;li&gt;Environments without DevOps automation for compilation&lt;/li&gt;
&lt;li&gt;Brownfield migrations where spec completeness is low&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Bigger Picture: A Shift in Gateway Philosophy
&lt;/h3&gt;

&lt;p&gt;Barbacane represents more than a new gateway. It's a philosophical shift:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Stop configuring your gateway to match your spec. Make your spec the configuration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This aligns with broader industry movements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code&lt;/strong&gt; → &lt;strong&gt;Behavior as Specification&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime validation&lt;/strong&gt; → &lt;strong&gt;Compile-time validation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration drift&lt;/strong&gt; → &lt;strong&gt;Configuration integrity&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not the only path forward (declarative gateways like KrakenD point in a similar direction), but Barbacane's Rust/WASM/FlatBuffers stack delivers uniquely strong safety and performance guarantees.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Barbacane's spec-driven model addresses a real pain point for API teams: keeping specs and gateway behavior in sync. By compiling the spec into the runtime artifact, that problem goes away entirely. The Rust and WASM foundation delivers strong performance and safety guarantees on top.&lt;/p&gt;

&lt;p&gt;The project is at v0.1.x, so it's best suited for new projects where you control the spec lifecycle. If your team already works OpenAPI-first with CI/CD automation, Barbacane fits naturally into that workflow.&lt;/p&gt;

&lt;p&gt;The goal: your API contract &lt;em&gt;is&lt;/em&gt; your production configuration. Security policies validated before deployment. Edge gateways starting in milliseconds with zero configuration drift. That's the direction we're heading.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Barbacane is open source and available at &lt;a href="https://github.com/barbacane-dev/barbacane" rel="noopener noreferrer"&gt;github.com/barbacane-dev/barbacane&lt;/a&gt;. As of February 2026, it remains an early-stage project—evaluate thoroughly before production use.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>barbacane</category>
      <category>apigateway</category>
      <category>rust</category>
      <category>webassembly</category>
    </item>
  </channel>
</rss>
