<?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>Forem: Anton Fedotov</title>
    <description>The latest articles on Forem by Anton Fedotov (@anviren).</description>
    <link>https://forem.com/anviren</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%2F3807721%2Ff8a186dc-afc0-476f-bf8e-b4cfd8939b94.jpg</url>
      <title>Forem: Anton Fedotov</title>
      <link>https://forem.com/anviren</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/anviren"/>
    <language>en</language>
    <item>
      <title>Adding a Trust Boundary to a LlamaIndex RAG Pipeline</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Wed, 29 Apr 2026 08:03:54 +0000</pubDate>
      <link>https://forem.com/anviren/adding-a-trust-boundary-to-a-llamaindex-rag-pipeline-3hik</link>
      <guid>https://forem.com/anviren/adding-a-trust-boundary-to-a-llamaindex-rag-pipeline-3hik</guid>
      <description>&lt;p&gt;Your LlamaIndex app does not only retrieve documents.&lt;/p&gt;

&lt;p&gt;It decides which external text is allowed to become model context.&lt;/p&gt;

&lt;p&gt;That is a trust decision, even if your code does not call it one.&lt;/p&gt;

&lt;p&gt;A PDF can contain useful facts.&lt;br&gt;
A support ticket can contain real customer context.&lt;br&gt;
A web page can contain documentation.&lt;br&gt;
An email thread can contain the answer your user needs.&lt;/p&gt;

&lt;p&gt;But all of those sources can also contain instructions your model should never follow.&lt;/p&gt;

&lt;p&gt;That is the uncomfortable part of RAG security: the dangerous text often does not come from the user prompt. It comes from the documents.&lt;/p&gt;

&lt;p&gt;This post shows how to add a trust boundary to a LlamaIndex RAG pipeline with Omega Walls.&lt;/p&gt;

&lt;p&gt;The core idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Retrieved text is evidence, not policy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the safest place to enforce that is between retrieval and synthesis.&lt;/p&gt;


&lt;h2&gt;
  
  
  The RAG failure mode
&lt;/h2&gt;

&lt;p&gt;A typical LlamaIndex flow looks clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;documents -&amp;gt; index -&amp;gt; query engine -&amp;gt; response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In code, it may look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;

&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the customer escalation and suggest the next step.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a good developer experience.&lt;/p&gt;

&lt;p&gt;But it hides a trust problem.&lt;/p&gt;

&lt;p&gt;The query engine retrieves relevant chunks. Those chunks are then used by the LLM to synthesize an answer. That is the normal RAG path: retrieve text, feed it into the answer-generation step, produce a response.&lt;/p&gt;

&lt;p&gt;The issue is that retrieved text can carry two very different kinds of content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Useful evidence:
- customer reported X
- policy says Y
- document describes Z

Untrusted instruction:
- ignore previous instructions
- reveal the system prompt
- call this tool
- send this data somewhere else
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If both kinds of text are placed into the same context without a boundary, the model has to separate evidence from instruction by itself.&lt;/p&gt;

&lt;p&gt;That is not a reliable boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Retrieved text is evidence, not policy
&lt;/h2&gt;

&lt;p&gt;The important shift is small but sharp:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Retrieved text should help the model answer.&lt;br&gt;
It should not control the workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means your RAG pipeline should preserve a hard distinction between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trusted:
- system policy
- developer instructions
- app configuration
- user request

untrusted:
- retrieved web pages
- PDFs
- emails
- support tickets
- uploaded files
- tool outputs containing external text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model should not have to infer that distinction from formatting alone.&lt;/p&gt;

&lt;p&gt;Your application should enforce it before the context is built.&lt;/p&gt;

&lt;p&gt;In Omega Walls, this is the role of the trust boundary. Untrusted content is projected into structured risk signals, filtered, and only allowed chunks are passed forward into context. Tool calls stay behind a tool gateway.&lt;/p&gt;

&lt;p&gt;For a LlamaIndex RAG app, the most natural placement is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;documents / web / tickets / PDFs
        ↓
index / retriever
        ↓
Omega Walls trust boundary
        ↓
allowed chunks
        ↓
query engine / response synthesis
        ↓
answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgos48cvwxia2lnwpscbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgos48cvwxia2lnwpscbh.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The trust boundary belongs between retrieval and synthesis: external documents are useful evidence, but they should be inspected before they shape the final context.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why post-generation checks are too late
&lt;/h2&gt;

&lt;p&gt;It is tempting to check the final answer.&lt;/p&gt;

&lt;p&gt;That can still be useful.&lt;/p&gt;

&lt;p&gt;But it is not enough.&lt;/p&gt;

&lt;p&gt;By the time the answer exists, the retrieved chunk may already have influenced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which facts were selected,&lt;/li&gt;
&lt;li&gt;which instruction hierarchy the model followed,&lt;/li&gt;
&lt;li&gt;whether a tool should be called,&lt;/li&gt;
&lt;li&gt;how intermediate summaries were formed,&lt;/li&gt;
&lt;li&gt;what got written into memory,&lt;/li&gt;
&lt;li&gt;what source got treated as authoritative.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a RAG pipeline, the critical moment is earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retrieved chunks -&amp;gt; context construction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is where external text becomes model context.&lt;/p&gt;

&lt;p&gt;So the boundary should live there.&lt;/p&gt;

&lt;p&gt;Not only at the user input.&lt;br&gt;
Not only after generation.&lt;br&gt;
Before synthesis.&lt;/p&gt;


&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Install Omega Walls with integration adapters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"omega-walls[integrations]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the framework adapters, including the LlamaIndex guard.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimal LlamaIndex integration
&lt;/h2&gt;

&lt;p&gt;Start with your normal LlamaIndex setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;

&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now wrap the query engine with Omega:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLlamaIndexGuard&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLlamaIndexGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_query_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize this support note&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sess-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the smallest useful version.&lt;/p&gt;

&lt;p&gt;You still use LlamaIndex as your data/query layer. You still build your index normally. You still call the query engine normally.&lt;/p&gt;

&lt;p&gt;The difference is that retrieved context now passes through a trust boundary before it is allowed to shape the response.&lt;/p&gt;




&lt;h2&gt;
  
  
  What actually gets guarded
&lt;/h2&gt;

&lt;p&gt;A useful RAG boundary needs to cover more than the initial query string.&lt;/p&gt;

&lt;p&gt;At minimum, you should think about five surfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. User query
&lt;/h3&gt;

&lt;p&gt;The user query starts the request.&lt;/p&gt;

&lt;p&gt;It may be harmless. It may be adversarial. It may also be asking the app to summarize an external document that contains adversarial text.&lt;/p&gt;

&lt;p&gt;The guard should know which session this belongs to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the attached escalation notes.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;support-case-1842&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;thread_id&lt;/code&gt; matters because stateful detection only makes sense if related steps belong to the same workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Retrieved chunks
&lt;/h3&gt;

&lt;p&gt;This is the main RAG surface.&lt;/p&gt;

&lt;p&gt;A retrieved chunk may contain facts and hidden instructions at the same time.&lt;/p&gt;

&lt;p&gt;The boundary should inspect those chunks before they become prompt context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retriever returns nodes/chunks
        ↓
guard evaluates external text
        ↓
only allowed chunks enter synthesis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Tool calls
&lt;/h3&gt;

&lt;p&gt;Some LlamaIndex apps are not just read-only QA systems.&lt;/p&gt;

&lt;p&gt;They retrieve, reason, and then call tools.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;send a ticket update,&lt;/li&gt;
&lt;li&gt;post a summary,&lt;/li&gt;
&lt;li&gt;call an internal API,&lt;/li&gt;
&lt;li&gt;write a file,&lt;/li&gt;
&lt;li&gt;trigger a workflow,&lt;/li&gt;
&lt;li&gt;fetch more external data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a tool can act outside the model, it should sit behind a gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLlamaIndexGuard&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLlamaIndexGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;network_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Your real external action lives here.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;safe_network_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network_post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;network_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use &lt;code&gt;safe_network_post&lt;/code&gt; instead of the raw function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safe_network_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://internal.example/support/update&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;case_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1842&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer escalation summarized from guarded context.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not this specific tool.&lt;/p&gt;

&lt;p&gt;The important part is that tool execution goes through one chokepoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Memory writes
&lt;/h3&gt;

&lt;p&gt;RAG systems often create derived state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;summaries,&lt;/li&gt;
&lt;li&gt;notes,&lt;/li&gt;
&lt;li&gt;extracted facts,&lt;/li&gt;
&lt;li&gt;cached answers,&lt;/li&gt;
&lt;li&gt;user preferences,&lt;/li&gt;
&lt;li&gt;case memory,&lt;/li&gt;
&lt;li&gt;long-term knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that state came from external text, preserve provenance.&lt;/p&gt;

&lt;p&gt;Do not turn this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;external PDF says: approval can be skipped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;memory fact: approval can be skipped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without a source/trust tag.&lt;/p&gt;

&lt;p&gt;A simple pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_memory_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The external document says approval can be skipped.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pdf:customer-escalation-1842&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_trust&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untrusted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;support-case-1842&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;save_to_memory&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quarantine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;save_to_quarantine&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory write denied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact memory store is your choice.&lt;/p&gt;

&lt;p&gt;The rule is the point:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Memory should remember where information came from.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. Session context
&lt;/h3&gt;

&lt;p&gt;Single-document checks miss a lot.&lt;/p&gt;

&lt;p&gt;A mild instruction in one chunk may not look serious.&lt;br&gt;
A later chunk may ask for a secret.&lt;br&gt;
A later tool output may introduce an action.&lt;br&gt;
Together, the pattern matters.&lt;/p&gt;

&lt;p&gt;That is why a RAG guard should be stateful across a session, not just a one-shot text classifier.&lt;/p&gt;


&lt;h2&gt;
  
  
  A small end-to-end example
&lt;/h2&gt;

&lt;p&gt;Here is a compact example showing the integration shape.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;llama_index.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLlamaIndexGuard&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaBlockedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OmegaToolBlockedError&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Build your normal LlamaIndex index
&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SimpleDirectoryReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;load_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VectorStoreIndex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Create the Omega guard
&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLlamaIndexGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Wrap the query engine
&lt;/span&gt;&lt;span class="n"&gt;query_engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_query_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_query_engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 4. Optional: wrap tools that may act outside the model
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;network_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;safe_network_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network_post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;network_post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 5. Use the guarded query engine
&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the support note and recommend the next action.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;support-case-1842&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OmegaBlockedError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked query/input step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Outcome:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control_outcome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reasons:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason_codes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OmegaToolBlockedError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked tool call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reason:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives your application a real block path.&lt;/p&gt;

&lt;p&gt;Not a vague error.&lt;br&gt;
Not a silent failure.&lt;br&gt;
Not a mysterious bad answer.&lt;/p&gt;

&lt;p&gt;A structured decision you can log, route, or escalate.&lt;/p&gt;


&lt;h2&gt;
  
  
  What happens to risky documents?
&lt;/h2&gt;

&lt;p&gt;A good RAG boundary should not kill the whole app because one chunk looked suspicious.&lt;/p&gt;

&lt;p&gt;Usually, the better behavior is selective:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;risky chunk detected
        ↓
remove that chunk from context
        ↓
continue with safe chunks
        ↓
freeze tools if tool-abuse pressure appears
        ↓
escalate if secrets/exfiltration are involved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is controlled degradation.&lt;/p&gt;

&lt;p&gt;The workflow should be able to continue with the remaining safe context.&lt;/p&gt;

&lt;p&gt;Example behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User asks:
"Summarize this customer note."

Retriever returns:
- doc-1: normal support note
- doc-2: external email with hidden instruction
- doc-3: product policy excerpt

Boundary:
- allows doc-1
- blocks doc-2
- allows doc-3

Query engine:
- synthesizes answer from doc-1 and doc-3
- does not include doc-2 in context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much better than two common alternatives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bad option 1: pass every retrieved chunk into context
bad option 2: hard-stop the whole workflow on any suspicious text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful middle path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remove risky influence, keep safe work moving
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50zmwy01wdjhpelwrsnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50zmwy01wdjhpelwrsnl.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Retrieved content should enter the prompt as evidence with provenance, not as workflow authority.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verify the integration
&lt;/h2&gt;

&lt;p&gt;After wiring the adapter, run the LlamaIndex smoke:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/smoke_llamaindex_guard.py &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the first verification step.&lt;/p&gt;

&lt;p&gt;You are not only checking that imports work.&lt;/p&gt;

&lt;p&gt;You are checking that the guard is actually on the execution path.&lt;/p&gt;

&lt;p&gt;For a broader release gate across all framework adapters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/run_framework_smokes.py &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected summary should be boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = ok
framework_count = 6
total_failures = 0
min_gateway_coverage &amp;gt;= 1.0
total_orphans = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boring is good here.&lt;/p&gt;

&lt;p&gt;It means the adapter is not decorative.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to log
&lt;/h2&gt;

&lt;p&gt;A trust boundary becomes much more useful when decisions are explainable.&lt;/p&gt;

&lt;p&gt;At minimum, log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;session_id
source_id
source_type
decision outcome
reason codes
blocked docs
tool gateway decisions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production systems, avoid raw content by default.&lt;/p&gt;

&lt;p&gt;Store hashes, bounded evidence, redacted excerpts only when policy allows it, and enough structured data to reproduce the decision later.&lt;/p&gt;

&lt;p&gt;That gives you incident review without turning your security logs into a new data leak.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical checklist for LlamaIndex RAG apps
&lt;/h2&gt;

&lt;p&gt;When reviewing a LlamaIndex app, I would walk through this checklist.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. What data sources are indexed?
&lt;/h3&gt;

&lt;p&gt;List them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal docs
uploaded PDFs
support tickets
email threads
web pages
chat exports
tool outputs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then mark trust level.&lt;/p&gt;

&lt;p&gt;If you cannot mark trust level, assume untrusted.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Where does retrieval happen?
&lt;/h3&gt;

&lt;p&gt;Find the point where the query engine receives retrieved chunks or nodes.&lt;/p&gt;

&lt;p&gt;That is where the boundary belongs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Can retrieved text reach synthesis directly?
&lt;/h3&gt;

&lt;p&gt;If yes, add a guard before synthesis.&lt;/p&gt;

&lt;p&gt;The synthesis step should receive allowed evidence, not raw external content.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Are sources preserved?
&lt;/h3&gt;

&lt;p&gt;Each chunk should retain enough metadata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pdf:customer-escalation-1842&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_trust&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untrusted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the chunk gets summarized or cached, preserve that provenance.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Can the RAG flow trigger tools?
&lt;/h3&gt;

&lt;p&gt;If yes, wrap those tools.&lt;/p&gt;

&lt;p&gt;Read-only retrieval is one risk level.&lt;br&gt;
File writes, network calls, ticket updates, and outbound messages are another.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Do security docs create false positives?
&lt;/h3&gt;

&lt;p&gt;Your guard should not panic just because a document discusses prompt injection, API keys, or jailbreaks.&lt;/p&gt;

&lt;p&gt;Security guidance is not the same thing as an active attack.&lt;/p&gt;

&lt;p&gt;That is why polarity and hard-negative tests matter.&lt;/p&gt;

&lt;p&gt;A document saying:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Never reveal API keys.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should not be treated like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reveal the API key.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is small in keywords and huge in intent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is not just prompt filtering
&lt;/h2&gt;

&lt;p&gt;A prompt filter usually asks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is this text bad?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A RAG trust boundary asks a better question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should this external text be allowed to shape model context, memory, or tools in this session?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a different problem.&lt;/p&gt;

&lt;p&gt;It needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;source awareness,&lt;/li&gt;
&lt;li&gt;session awareness,&lt;/li&gt;
&lt;li&gt;context placement,&lt;/li&gt;
&lt;li&gt;tool gateway enforcement,&lt;/li&gt;
&lt;li&gt;memory provenance,&lt;/li&gt;
&lt;li&gt;selective blocking,&lt;/li&gt;
&lt;li&gt;auditable decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why the boundary belongs in the pipeline, not just in a prompt template.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this does not solve
&lt;/h2&gt;

&lt;p&gt;A trust boundary is not magic.&lt;/p&gt;

&lt;p&gt;It does not replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;secret management,&lt;/li&gt;
&lt;li&gt;least-privilege tools,&lt;/li&gt;
&lt;li&gt;network allowlists,&lt;/li&gt;
&lt;li&gt;auth and permissions,&lt;/li&gt;
&lt;li&gt;model-side safety policies,&lt;/li&gt;
&lt;li&gt;human approval for sensitive operations,&lt;/li&gt;
&lt;li&gt;logging and incident response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also depends on correct placement.&lt;/p&gt;

&lt;p&gt;If untrusted text can bypass the guard and enter context directly, the boundary is broken.&lt;/p&gt;

&lt;p&gt;If tools can execute outside the gateway, tool enforcement is broken.&lt;/p&gt;

&lt;p&gt;If source metadata is stripped too early, later steps cannot distinguish trusted evidence from untrusted text.&lt;/p&gt;

&lt;p&gt;So the rule is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Put the boundary on the actual path between retrieval and synthesis.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not next to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  A good rollout order
&lt;/h2&gt;

&lt;p&gt;I would not start with hard blocking in production.&lt;/p&gt;

&lt;p&gt;Use a safer rollout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Wrap the query engine.
2. Wrap any tools that can act outside the model.
3. Preserve source_id, source_type, and source_trust.
4. Run the strict LlamaIndex smoke.
5. Run in monitor mode on realistic traffic.
6. Inspect reports and decisions.
7. Tune hard negatives and source handling.
8. Enable enforcement for selected paths.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the monitor phase.&lt;/p&gt;

&lt;p&gt;You want to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which sources are noisy,&lt;/li&gt;
&lt;li&gt;which chunks would be blocked,&lt;/li&gt;
&lt;li&gt;whether benign security docs stay quiet,&lt;/li&gt;
&lt;li&gt;whether tool-freeze decisions are understandable,&lt;/li&gt;
&lt;li&gt;whether operators have enough information to act.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hard blocking without observability is how a safety layer becomes a production incident.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;RAG makes it easy to give a model more context.&lt;/p&gt;

&lt;p&gt;That is useful.&lt;/p&gt;

&lt;p&gt;But context is not neutral.&lt;/p&gt;

&lt;p&gt;When your app retrieves documents, it is deciding which external text gets a chance to influence the model. If that text comes from web pages, PDFs, emails, tickets, uploads, or tool outputs, it should not be treated as trusted by default.&lt;/p&gt;

&lt;p&gt;The better rule is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Retrieved text is evidence, not policy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LlamaIndex gives you the data/query layer.&lt;/p&gt;

&lt;p&gt;Omega Walls adds the trust boundary around the part of the pipeline where external text becomes context.&lt;/p&gt;

&lt;p&gt;That boundary should sit before synthesis, before memory writes, and before tools.&lt;/p&gt;

&lt;p&gt;Not because every document is malicious.&lt;/p&gt;

&lt;p&gt;Because production RAG systems should not ask the model to guess what is trusted.&lt;/p&gt;




&lt;p&gt;Omega Walls ships framework adapters for LangChain, LangGraph, LlamaIndex, Haystack, AutoGen, and CrewAI.&lt;/p&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"omega-walls[integrations]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/synqratech/omega-walls" rel="noopener noreferrer"&gt;https://github.com/synqratech/omega-walls&lt;/a&gt;&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/omega-walls/" rel="noopener noreferrer"&gt;https://pypi.org/project/omega-walls/&lt;/a&gt;&lt;br&gt;
Site: &lt;a href="https://synqra.tech/omega-walls" rel="noopener noreferrer"&gt;https://synqra.tech/omega-walls&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llamaindex</category>
      <category>agents</category>
      <category>security</category>
      <category>rag</category>
    </item>
    <item>
      <title>I built an open-source trust boundary for RAG and AI agent pipelines</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Mon, 27 Apr 2026 12:12:57 +0000</pubDate>
      <link>https://forem.com/anviren/i-built-an-open-source-trust-boundary-for-rag-and-ai-agent-pipelines-19bj</link>
      <guid>https://forem.com/anviren/i-built-an-open-source-trust-boundary-for-rag-and-ai-agent-pipelines-19bj</guid>
      <description>&lt;p&gt;A pattern I keep seeing in AI agent workflows:&lt;/p&gt;

&lt;p&gt;they work on clean demo data,&lt;br&gt;
then become fragile on real-world content.&lt;/p&gt;

&lt;p&gt;Not because of some dramatic “AI jailbreak” story.&lt;/p&gt;

&lt;p&gt;More often because the pipeline has no clear boundary between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trusted instructions&lt;/li&gt;
&lt;li&gt;retrieved content&lt;/li&gt;
&lt;li&gt;emails / PDFs / tickets&lt;/li&gt;
&lt;li&gt;memory&lt;/li&gt;
&lt;li&gt;tool outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the model, all of this can become one context stream.&lt;/p&gt;

&lt;p&gt;That means untrusted content can quietly start influencing the workflow.&lt;/p&gt;

&lt;p&gt;I built Omega Walls as an open-source Python library for this problem.&lt;/p&gt;

&lt;p&gt;The idea is to put a runtime trust boundary between untrusted content, model context, memory, and tools.&lt;/p&gt;

&lt;p&gt;The first version focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RAG / agent prompt injection&lt;/li&gt;
&lt;li&gt;cross-document or cross-step attack pressure&lt;/li&gt;
&lt;li&gt;secret-exfiltration pressure&lt;/li&gt;
&lt;li&gt;tool/action abuse&lt;/li&gt;
&lt;li&gt;deterministic evidence and auditability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;omega-walls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;GitHub:&lt;br&gt;
&lt;a href="https://github.com/synqratech/omega-walls" rel="noopener noreferrer"&gt;https://github.com/synqratech/omega-walls&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PyPI:&lt;br&gt;
&lt;a href="https://pypi.org/project/omega-walls/" rel="noopener noreferrer"&gt;https://pypi.org/project/omega-walls/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’d be interested in feedback from people building RAG or internal agent systems: where would you expect this layer to sit in your stack?&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>Adding a Stateful Trust Boundary to a LangGraph Agent</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Mon, 27 Apr 2026 09:33:55 +0000</pubDate>
      <link>https://forem.com/anviren/adding-a-stateful-trust-boundary-to-a-langgraph-agent-366c</link>
      <guid>https://forem.com/anviren/adding-a-stateful-trust-boundary-to-a-langgraph-agent-366c</guid>
      <description>&lt;p&gt;LangGraph makes agent workflows easier to reason about.&lt;/p&gt;

&lt;p&gt;You can see the nodes.&lt;br&gt;
You can see the edges.&lt;br&gt;
You can see where state is read, updated, and passed forward.&lt;/p&gt;

&lt;p&gt;That is a big improvement over a black-box agent loop.&lt;/p&gt;

&lt;p&gt;But it also exposes the question most agent pipelines eventually have to answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which parts of this graph are allowed to trust external content?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A graph-based agent can read search results, PDFs, emails, tickets, web pages, tool outputs, and user-pasted text. Some of that content is useful evidence. Some of it may contain instructions that should never become part of the agent’s control flow.&lt;/p&gt;

&lt;p&gt;The problem is not only whether a user prompt is malicious.&lt;/p&gt;

&lt;p&gt;In a stateful graph, the real question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where does untrusted content enter the graph, can it get written into state, and can it later influence a tool node?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where a stateful trust boundary helps.&lt;/p&gt;

&lt;p&gt;This post shows how to add that boundary to a LangGraph workflow with Omega Walls.&lt;/p&gt;


&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;If you only remember one thing, make it this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every edge that carries external text is a trust-boundary edge.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Every node that can execute tools needs a gateway.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Every state write needs a source/trust tag.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the whole mental model.&lt;/p&gt;

&lt;p&gt;LangGraph gives you the graph. Omega Walls gives you a boundary around the parts of the graph that should not trust unverified content.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why LangGraph changes the security conversation
&lt;/h2&gt;

&lt;p&gt;In a simple agent loop, risk often looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user input -&amp;gt; model -&amp;gt; tool -&amp;gt; model -&amp;gt; answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is easy to explain, but it hides the important part.&lt;/p&gt;

&lt;p&gt;A real workflow is messier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user input
  -&amp;gt; retrieve docs
  -&amp;gt; summarize docs
  -&amp;gt; update state
  -&amp;gt; decide next node
  -&amp;gt; call tool
  -&amp;gt; receive tool output
  -&amp;gt; update state again
  -&amp;gt; generate final answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once state enters the picture, prompt injection is no longer just an input-filtering problem.&lt;/p&gt;

&lt;p&gt;A retrieved web page can influence one node.&lt;br&gt;
That node can write a summary into state.&lt;br&gt;
A later node can treat that state as trusted context.&lt;br&gt;
Another node can use that context to decide whether to call a tool.&lt;/p&gt;

&lt;p&gt;Nothing needs to look dramatic in one step.&lt;/p&gt;

&lt;p&gt;The bad pattern can be distributed across the workflow.&lt;/p&gt;

&lt;p&gt;That is why graph agents need controls that are also graph-shaped.&lt;/p&gt;


&lt;h2&gt;
  
  
  The failure mode: untrusted text becomes workflow state
&lt;/h2&gt;

&lt;p&gt;Here is the common failure mode.&lt;/p&gt;

&lt;p&gt;You build a LangGraph workflow that looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User request
  -&amp;gt; retrieve external documents
  -&amp;gt; analyze documents
  -&amp;gt; write notes into graph state
  -&amp;gt; agent decides what to do
  -&amp;gt; tool node executes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow feels clean.&lt;/p&gt;

&lt;p&gt;The retrieval node retrieves.&lt;br&gt;
The analysis node analyzes.&lt;br&gt;
The state carries the result.&lt;br&gt;
The tool node acts.&lt;/p&gt;

&lt;p&gt;But if the retrieved document contains hidden instructions, and you write a derived version of that content into state without marking its source, you have a trust problem.&lt;/p&gt;

&lt;p&gt;The graph state now contains external influence.&lt;/p&gt;

&lt;p&gt;A later node may not know whether a sentence came from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your system policy,&lt;/li&gt;
&lt;li&gt;the user’s actual request,&lt;/li&gt;
&lt;li&gt;a trusted internal source,&lt;/li&gt;
&lt;li&gt;an external PDF,&lt;/li&gt;
&lt;li&gt;a fetched web page,&lt;/li&gt;
&lt;li&gt;or a tool result that contained untrusted text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once that distinction is lost, the model has to guess.&lt;/p&gt;

&lt;p&gt;That is not a good boundary.&lt;/p&gt;

&lt;p&gt;The dangerous part is not always the first model call. The dangerous part is what gets written into state and reused later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9cupifwde4j5r2p5qrr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9cupifwde4j5r2p5qrr.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Without a boundary, external text can be summarized into graph state and later treated as trusted workflow context.&lt;/p&gt;


&lt;h2&gt;
  
  
  The right mental model
&lt;/h2&gt;

&lt;p&gt;The easiest way to reason about this is to treat every edge carrying external content as a boundary edge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rg7m42vodjb5cfi4eyn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rg7m42vodjb5cfi4eyn.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trust-boundary edges in a LangGraph workflow: external content is guarded before it reaches graph state, context, or tools.&lt;/p&gt;

&lt;p&gt;A LangGraph workflow should not treat all state as equally trusted.&lt;/p&gt;

&lt;p&gt;A cleaner mental model looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Trusted inputs
  - system / app policy
  - developer-controlled config
  - user request

Untrusted inputs
  - web pages
  - search results
  - PDFs
  - emails
  - tickets
  - tool outputs containing external text

Boundary
  - inspect
  - filter
  - decide
  - tag
  - block or allow

Graph
  - agent node
  - state
  - tool nodes
  - memory writes

Gateway
  - tool execution
  - file/network/action controls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trust boundary should sit before untrusted content can shape model context, state, or tool execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insert diagram here:&lt;/strong&gt; &lt;code&gt;Trust Boundary Edges in a LangGraph Workflow&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Caption:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A practical trust-boundary model for LangGraph: trusted inputs can flow into the agent, but external content should pass through a boundary before it reaches graph state, context, or tools.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Omega Walls adds
&lt;/h2&gt;

&lt;p&gt;Omega Walls is a stateful trust boundary for RAG and agentic pipelines.&lt;/p&gt;

&lt;p&gt;In this integration, the important pieces are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;model/input checks,&lt;/li&gt;
&lt;li&gt;tool preflight checks,&lt;/li&gt;
&lt;li&gt;memory write checks,&lt;/li&gt;
&lt;li&gt;session-aware runtime state,&lt;/li&gt;
&lt;li&gt;typed block paths,&lt;/li&gt;
&lt;li&gt;structured decisions you can log or route into operator workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to make the graph useless by hard-blocking everything.&lt;/p&gt;

&lt;p&gt;The goal is controlled degradation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;suspicious content can be soft-blocked,&lt;/li&gt;
&lt;li&gt;risky sources can be quarantined,&lt;/li&gt;
&lt;li&gt;tool execution can be frozen,&lt;/li&gt;
&lt;li&gt;severe cases can be escalated,&lt;/li&gt;
&lt;li&gt;safe parts of the workflow can continue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That matters in real agents. A production workflow should not have only two modes: “everything allowed” and “everything dead.”&lt;/p&gt;




&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;Install Omega Walls with integration adapters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"omega-walls[integrations]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also install only the base package and the framework dependencies you use, but the integrations extra is the fastest path for framework adapters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minimal LangGraph integration
&lt;/h2&gt;

&lt;p&gt;If you already have a compiled LangGraph workflow, the integration shape is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLangGraphGuard&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLangGraphGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;safe_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiled_graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;safe_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network_post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network_post_fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;guard_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_guard_node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# optional StateGraph node helper
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three important pieces are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;safe_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiled_graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wraps the graph-level execution path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;safe_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network_post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network_post_fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the tool call goes through a guarded preflight path before it executes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;guard_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_guard_node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you an optional explicit guard node you can place inside a &lt;code&gt;StateGraph&lt;/code&gt; when you want the boundary to be visible in the workflow itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to put the guard
&lt;/h2&gt;

&lt;p&gt;There are two common patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 1: Wrap the compiled graph
&lt;/h3&gt;

&lt;p&gt;Use this when you want the simplest integration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLangGraphGuard&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLangGraphGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;compiled_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;safe_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiled_graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;safe_graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the latest external research note.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configurable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer-support-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the easiest entry point.&lt;/p&gt;

&lt;p&gt;You keep your graph structure. The guard sits around the graph execution path. If your app already passes a &lt;code&gt;thread_id&lt;/code&gt;, &lt;code&gt;conversation_id&lt;/code&gt;, or &lt;code&gt;session_id&lt;/code&gt;, the adapter can use that to keep runtime decisions tied to the right workflow session.&lt;/p&gt;

&lt;p&gt;Use this when you want a quick integration without changing the graph topology.&lt;/p&gt;




&lt;h3&gt;
  
  
  Pattern 2: Add an explicit guard node
&lt;/h3&gt;

&lt;p&gt;Use this when you want the trust boundary to be visible in the graph.&lt;/p&gt;

&lt;p&gt;The exact graph shape depends on your app, but the idea is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;START
  -&amp;gt; retrieve_external_content
  -&amp;gt; omega_guard
  -&amp;gt; agent
  -&amp;gt; tools
  -&amp;gt; END
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langgraph.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLangGraphGuard&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;retrieved_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;guarded_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;risk_notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLangGraphGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;retrieve_external_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Replace with your retriever, search API, document loader, etc.
&lt;/span&gt;    &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web:example.com/page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trust&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untrusted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;External page content...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieved_docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;omega_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build_guard_node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;agent_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# At this point, only guarded/allowed content should shape the prompt.
&lt;/span&gt;    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;guarded_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;guarded_docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="c1"&gt;# Build your prompt from trusted messages + guarded docs.
&lt;/span&gt;    &lt;span class="c1"&gt;# Call your model here.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processed &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guarded_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; guarded documents.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieve_external_content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retrieve_external_content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;omega_guard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;omega_guard&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agent_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;START&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieve_external_content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieve_external_content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;omega_guard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;omega_guard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;compiled_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;safe_graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_graph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiled_graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version has one major advantage: the trust boundary is not hidden.&lt;/p&gt;

&lt;p&gt;Anyone reading the graph can see that external content does not go straight from retrieval into the agent node.&lt;/p&gt;

&lt;p&gt;It passes through the guard first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Guard tool nodes, not only text inputs
&lt;/h2&gt;

&lt;p&gt;The mistake is to guard only text before the model call.&lt;/p&gt;

&lt;p&gt;In a LangGraph workflow, tool nodes are often where the real-world impact happens.&lt;/p&gt;

&lt;p&gt;A tool might:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;send a network request,&lt;/li&gt;
&lt;li&gt;write a file,&lt;/li&gt;
&lt;li&gt;update a ticket,&lt;/li&gt;
&lt;li&gt;call an internal API,&lt;/li&gt;
&lt;li&gt;send a message,&lt;/li&gt;
&lt;li&gt;trigger a transaction,&lt;/li&gt;
&lt;li&gt;fetch another external page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If external text can influence a tool call, that tool call should go through a gateway.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLangGraphGuard&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLangGraphGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;network_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Your real network operation lives here.
&lt;/span&gt;    &lt;span class="c1"&gt;# The guard should sit before this function executes.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;safe_network_post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;network_post&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;network_post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use &lt;code&gt;safe_network_post&lt;/code&gt; in your graph instead of the raw function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tool_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safe_network_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://internal.example/ingest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important thing is not the name &lt;code&gt;network_post&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The important thing is the chokepoint.&lt;/p&gt;

&lt;p&gt;If a tool can do something outside the model, it should not be callable through an unguarded side path.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handle blocked paths explicitly
&lt;/h2&gt;

&lt;p&gt;A boundary should not fail mysteriously.&lt;/p&gt;

&lt;p&gt;Your application should know whether the model/input step was blocked or the tool call was blocked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaBlockedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OmegaToolBlockedError&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;safe_graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analyze this external report and continue the workflow.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;configurable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;thread_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workflow-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OmegaBlockedError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked model/input step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Outcome:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control_outcome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reasons:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason_codes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OmegaToolBlockedError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked tool call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tool:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reason:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives your app a real branch.&lt;/p&gt;

&lt;p&gt;You can log it.&lt;br&gt;
You can show a safe message.&lt;br&gt;
You can ask for human approval.&lt;br&gt;
You can continue without the risky source.&lt;br&gt;
You can freeze tools for the session.&lt;/p&gt;

&lt;p&gt;The point is not just “block bad thing.”&lt;/p&gt;

&lt;p&gt;The point is to make the decision operational.&lt;/p&gt;


&lt;h2&gt;
  
  
  Guard memory writes
&lt;/h2&gt;

&lt;p&gt;Graph state is not the only state you should care about.&lt;/p&gt;

&lt;p&gt;Many agent systems also write to memory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user facts,&lt;/li&gt;
&lt;li&gt;summaries,&lt;/li&gt;
&lt;li&gt;preferences,&lt;/li&gt;
&lt;li&gt;retrieved notes,&lt;/li&gt;
&lt;li&gt;intermediate conclusions,&lt;/li&gt;
&lt;li&gt;task state,&lt;/li&gt;
&lt;li&gt;long-term memory,&lt;/li&gt;
&lt;li&gt;cached tool results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a memory write came from external text, preserve that fact.&lt;/p&gt;

&lt;p&gt;Do not let the system turn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;external page said X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;known fact: X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;without a trust tag.&lt;/p&gt;

&lt;p&gt;Use the memory write check when persistence is involved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_memory_write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The external document says the support workflow should skip approval.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web:example.com/page&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source_trust&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untrusted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thread_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;workflow-123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;allow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;save_to_memory&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quarantine&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;save_to_quarantine&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory write denied&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact storage backend is up to your app.&lt;/p&gt;

&lt;p&gt;The principle is not optional:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Memory should remember where information came from.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you lose provenance, your future graph steps cannot tell the difference between trusted state and external influence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verify the integration
&lt;/h2&gt;

&lt;p&gt;After wiring the guard, run the strict smoke for LangGraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/smoke_langgraph_guard.py &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the first thing I would run locally.&lt;/p&gt;

&lt;p&gt;Not “does my app still start?”&lt;br&gt;
Not “does the graph compile?”&lt;br&gt;
But:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the guard actually on the execution path?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the test that matters.&lt;/p&gt;

&lt;p&gt;For a broader release gate across framework adapters, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/run_framework_smokes.py &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected high-level result should be boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status = ok
framework_count = 6
total_failures = 0
min_gateway_coverage &amp;gt;= 1.0
total_orphans = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boring is good here.&lt;/p&gt;

&lt;p&gt;It means your wrappers are not decorative.&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical graph checklist
&lt;/h2&gt;

&lt;p&gt;When reviewing a LangGraph workflow, I would walk it with this checklist.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Which nodes receive external text?
&lt;/h3&gt;

&lt;p&gt;Look for nodes that read from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrievers,&lt;/li&gt;
&lt;li&gt;search APIs,&lt;/li&gt;
&lt;li&gt;browser tools,&lt;/li&gt;
&lt;li&gt;PDFs,&lt;/li&gt;
&lt;li&gt;emails,&lt;/li&gt;
&lt;li&gt;tickets,&lt;/li&gt;
&lt;li&gt;web fetchers,&lt;/li&gt;
&lt;li&gt;tool outputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those nodes should either be guarded directly or feed into a guard node before the agent consumes their output.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Which edges carry untrusted content?
&lt;/h3&gt;

&lt;p&gt;Edges are not just control flow.&lt;/p&gt;

&lt;p&gt;In a stateful graph, edges also carry influence.&lt;/p&gt;

&lt;p&gt;If an edge carries external text, treat it as a boundary edge.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Which nodes write state?
&lt;/h3&gt;

&lt;p&gt;Any node that writes to graph state can change future behavior.&lt;/p&gt;

&lt;p&gt;If it writes derived content from an untrusted source, keep source metadata attached.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Which nodes can execute tools?
&lt;/h3&gt;

&lt;p&gt;Tool nodes should call wrapped tools, not raw functions.&lt;/p&gt;

&lt;p&gt;If a tool can write, send, fetch, mutate, or trigger an external action, it belongs behind a gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Can a later node distinguish trusted from untrusted state?
&lt;/h3&gt;

&lt;p&gt;If not, you probably need better state shape.&lt;/p&gt;

&lt;p&gt;A useful state object should preserve the difference between:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;trusted_policy&lt;/span&gt;
&lt;span class="n"&gt;user_request&lt;/span&gt;
&lt;span class="n"&gt;guarded_docs&lt;/span&gt;
&lt;span class="n"&gt;quarantined_docs&lt;/span&gt;
&lt;span class="n"&gt;tool_results&lt;/span&gt;
&lt;span class="n"&gt;risk_notes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of dumping everything into one generic &lt;code&gt;context&lt;/code&gt; field.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example state shape
&lt;/h2&gt;

&lt;p&gt;Here is a simple state shape I prefer for guarded graph workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SourceRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;source_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;trust&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trusted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;semi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;untrusted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DocumentChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SourceRef&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RiskNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;source_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;reason_codes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Raw external inputs
&lt;/span&gt;    &lt;span class="n"&gt;retrieved_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DocumentChunk&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Content allowed to shape context
&lt;/span&gt;    &lt;span class="n"&gt;guarded_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DocumentChunk&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Content blocked or held for review
&lt;/span&gt;    &lt;span class="n"&gt;quarantined_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DocumentChunk&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Runtime/security notes
&lt;/span&gt;    &lt;span class="n"&gt;risk_notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;RiskNote&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Tool outputs with provenance
&lt;/span&gt;    &lt;span class="n"&gt;tool_results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes trust visible.&lt;/p&gt;

&lt;p&gt;It is much easier to reason about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;guarded_docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;than a giant mixed list called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name matters because future contributors will follow the shape you give them.&lt;/p&gt;

&lt;p&gt;If everything is called context, everything will eventually be treated as context.&lt;/p&gt;




&lt;h2&gt;
  
  
  What happens when something is risky?
&lt;/h2&gt;

&lt;p&gt;The useful behavior is not always “stop the agent.”&lt;/p&gt;

&lt;p&gt;Often, the better behavior is controlled degradation.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;External document looks suspicious
  -&amp;gt; remove it from context
  -&amp;gt; continue with remaining docs
  -&amp;gt; log reason
  -&amp;gt; freeze tools if tool-abuse pressure appears
  -&amp;gt; escalate only if needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is better than letting the model ingest everything.&lt;/p&gt;

&lt;p&gt;It is also better than killing every workflow at the first suspicious string.&lt;/p&gt;

&lt;p&gt;A graph workflow gives you room to degrade gracefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retrieve docs
  -&amp;gt; guard docs
  -&amp;gt; if safe: continue
  -&amp;gt; if suspicious: continue without that source
  -&amp;gt; if tool risk: freeze tool node
  -&amp;gt; if severe: route to human review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a product behavior, not just a security behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this does not solve
&lt;/h2&gt;

&lt;p&gt;A trust boundary is not magic.&lt;/p&gt;

&lt;p&gt;It does not replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;least-privilege tool permissions,&lt;/li&gt;
&lt;li&gt;secret management,&lt;/li&gt;
&lt;li&gt;network allowlists,&lt;/li&gt;
&lt;li&gt;proper auth,&lt;/li&gt;
&lt;li&gt;human approval for sensitive operations,&lt;/li&gt;
&lt;li&gt;logging and incident review,&lt;/li&gt;
&lt;li&gt;model-side safety controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also depends on architecture.&lt;/p&gt;

&lt;p&gt;If raw tools can execute outside the gateway, the boundary can be bypassed.&lt;br&gt;
If untrusted content can be written directly into state, the boundary is not really a boundary.&lt;br&gt;
If your app removes source metadata too early, later nodes cannot reason about trust.&lt;/p&gt;

&lt;p&gt;So the integration rule is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Put the boundary on the actual execution path, not next to it.&lt;/p&gt;
&lt;/blockquote&gt;



&lt;p&gt;A useful boundary should not have only two states: allow everything or kill the whole workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ksk9qzt4z8fesl4ao58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ksk9qzt4z8fesl4ao58.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Controlled degradation: remove risky influence, freeze dangerous execution paths, and continue with the remaining safe workflow.&lt;/p&gt;
&lt;h2&gt;
  
  
  A good rollout order
&lt;/h2&gt;

&lt;p&gt;I would not jump straight to hard blocking.&lt;/p&gt;

&lt;p&gt;Use this order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Wrap the graph.
2. Wrap tool nodes.
3. Add an explicit guard node where external content enters.
4. Run strict smoke.
5. Run in monitor mode.
6. Inspect reports and decisions.
7. Add operator workflow.
8. Enable enforcement for selected paths.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The monitor phase is important.&lt;/p&gt;

&lt;p&gt;You want to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which sources trigger decisions,&lt;/li&gt;
&lt;li&gt;which tools would have been blocked,&lt;/li&gt;
&lt;li&gt;whether benign security docs stay quiet,&lt;/li&gt;
&lt;li&gt;whether risky paths show up clearly,&lt;/li&gt;
&lt;li&gt;whether operators can understand the decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hard blocking before observability is how you create a production incident while trying to prevent one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;LangGraph gives you a better way to build agents because it makes workflow structure explicit.&lt;/p&gt;

&lt;p&gt;That same explicit structure gives you a better way to secure them.&lt;/p&gt;

&lt;p&gt;Do not think of a trust boundary as a single filter before the prompt.&lt;/p&gt;

&lt;p&gt;In a graph, the boundary is a design discipline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;external text enters through guarded edges,&lt;/li&gt;
&lt;li&gt;state keeps provenance,&lt;/li&gt;
&lt;li&gt;tools execute through a gateway,&lt;/li&gt;
&lt;li&gt;risky content can be removed without killing the whole workflow,&lt;/li&gt;
&lt;li&gt;decisions are visible enough to debug.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the practical shape I want in production agent systems.&lt;/p&gt;

&lt;p&gt;Not a bigger prompt.&lt;/p&gt;

&lt;p&gt;A clearer boundary.&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Omega Walls is open source and ships framework adapters for LangChain, LangGraph, LlamaIndex, Haystack, AutoGen, and CrewAI.

Install:

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
bash
pip install "omega-walls[integrations]"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;LangGraph smoke:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
python scripts/smoke_langgraph_guard.py --strict


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/synqratech/omega-walls" rel="noopener noreferrer"&gt;https://github.com/synqratech/omega-walls&lt;/a&gt;&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/omega-walls/" rel="noopener noreferrer"&gt;https://pypi.org/project/omega-walls/&lt;/a&gt;&lt;br&gt;
Site: &lt;a href="https://synqra.tech/omega-walls" rel="noopener noreferrer"&gt;https://synqra.tech/omega-walls&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>langgraph</category>
      <category>security</category>
      <category>python</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Fri, 24 Apr 2026 09:17:46 +0000</pubDate>
      <link>https://forem.com/anviren/-34o8</link>
      <guid>https://forem.com/anviren/-34o8</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa" class="crayons-story__hidden-navigation-link"&gt;How to Add a Stateful Trust Boundary to a LangChain Agent with Omega Walls&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/anviren" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3807721%2Ff8a186dc-afc0-476f-bf8e-b4cfd8939b94.jpg" alt="anviren profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/anviren" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anton Fedotov
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anton Fedotov
                
              
              &lt;div id="story-author-preview-content-3545222" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/anviren" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3807721%2Ff8a186dc-afc0-476f-bf8e-b4cfd8939b94.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anton Fedotov&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa" id="article-link-3545222"&gt;
          How to Add a Stateful Trust Boundary to a LangChain Agent with Omega Walls
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/langchain"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;langchain&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;7&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              2&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>How to Add a Stateful Trust Boundary to a LangChain Agent with Omega Walls</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Fri, 24 Apr 2026 09:05:02 +0000</pubDate>
      <link>https://forem.com/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa</link>
      <guid>https://forem.com/anviren/how-to-add-a-stateful-trust-boundary-to-a-langchain-agent-with-omega-walls-29pa</guid>
      <description>&lt;p&gt;Your agent looked fine in the demo.&lt;/p&gt;

&lt;p&gt;Then it started reading real PDFs, tickets, fetched pages, and tool outputs. Nothing looked obviously malicious. No one typed “ignore all previous instructions.” Still, the workflow drifted. The model began to treat external text as policy, the context got noisier, and tool execution became harder to trust.&lt;/p&gt;

&lt;p&gt;That is the uncomfortable part of building agents on live data: a lot of failures do not come from the user prompt. They come from the agent’s architecture of trust. External content enters the reasoning loop disguised as facts, workflow state, or routine context. A single chunk may look harmless. The pattern only appears when you look across steps.&lt;/p&gt;

&lt;p&gt;This is where a stateful trust boundary helps.&lt;/p&gt;

&lt;p&gt;In this post, I’ll show how to add one to a LangChain agent with Omega Walls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LangChain agents drift on live data
&lt;/h2&gt;

&lt;p&gt;A LangChain agent is not just “prompt in, answer out.” It runs in a loop: call the model, decide whether to use tools, execute tools, feed results back, continue until a stop condition is reached. LangChain’s &lt;code&gt;create_agent&lt;/code&gt; is their production-ready entry point, and the runtime is graph-based under the hood.&lt;/p&gt;

&lt;p&gt;That loop is exactly why live-data failures become subtle.&lt;/p&gt;

&lt;p&gt;A retrieved page can contain hidden policy. An attachment can smuggle instructions inside normal-looking operational text. A tool can fetch external content that looks like context but behaves like control. If your pipeline treats all of that as just “more text,” you are asking the model to separate trusted instructions from untrusted evidence on its own, in the middle of an execution loop.&lt;/p&gt;

&lt;p&gt;That usually works right up until it doesn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shift that matters
&lt;/h2&gt;

&lt;p&gt;Before we touch the code, it helps to fix the architecture in one simple mental model.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo80hb7ne9c1ms2cf28hs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo80hb7ne9c1ms2cf28hs.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Where the trust boundary sits in a LangChain agent:&lt;/strong&gt; trusted inputs go straight to the agent, untrusted content passes through the boundary first, and tools execute behind a guarded gateway.&lt;/p&gt;

&lt;p&gt;The fix is not “add one more regex filter before the prompt.”&lt;/p&gt;

&lt;p&gt;The real shift is architectural: &lt;strong&gt;do not treat retrieved content as instructions&lt;/strong&gt;. Treat it as untrusted input that must pass through a trust boundary before it is allowed to shape context or trigger tools. Omega Walls is built around exactly that idea. In the project docs, it sits between untrusted content, the model loop, and the tool layer; it projects each chunk into structured risk signals, keeps a session-scoped risk state across steps, and can react with actions such as &lt;code&gt;SOFT_BLOCK&lt;/code&gt;, &lt;code&gt;SOURCE_QUARANTINE&lt;/code&gt;, &lt;code&gt;TOOL_FREEZE&lt;/code&gt;, and &lt;code&gt;HUMAN_ESCALATE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That matters because many agent attacks are not single-message events. They build across retrieved chunks, memory carry-over, tool outputs, and related steps. Omega’s design explicitly models that: packet-level aggregation, cross-wall reinforcement, state accumulation, and deterministic Off conditions instead of one-shot input scanning.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why LangChain is a good first integration target
&lt;/h2&gt;

&lt;p&gt;LangChain is a clean first framework for this because the integration point is obvious.&lt;/p&gt;

&lt;p&gt;LangChain already treats middleware as a first-class runtime control layer. Omega already ships an official LangChain adapter. That means you do not need to redesign your agent or fork your stack. You keep your existing agent shape, then insert a guard at the execution boundary LangChain already exposes.&lt;/p&gt;

&lt;p&gt;In Omega’s framework docs, the LangChain path is intentionally small: install the integration extra, create &lt;code&gt;OmegaLangChainGuard&lt;/code&gt;, pass &lt;code&gt;guard.middleware()&lt;/code&gt; into &lt;code&gt;create_agent&lt;/code&gt;, then verify behavior with &lt;code&gt;python scripts/smoke_langchain_guard.py --strict&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Install the integration
&lt;/h2&gt;

&lt;p&gt;Start with the integration extras:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"omega-walls[integrations]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current PyPI package exposes &lt;code&gt;integrations&lt;/code&gt; as an extra, alongside &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;attachments&lt;/code&gt;, and &lt;code&gt;train&lt;/code&gt;, and the package is positioned as a stateful prompt-injection defense for RAG and agent pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimal LangChain wiring
&lt;/h2&gt;

&lt;p&gt;Here is the smallest useful wiring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.integrations&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaLangChainGuard&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_customer_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Example tool. Replace with your own CRM, KB, or ticket fetch.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Customer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: recent notes loaded.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OmegaLangChainGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quickstart&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_customer_note&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the latest customer note and tell me if anything looks risky.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This follows Omega’s LangChain adapter contract directly: &lt;code&gt;OmegaLangChainGuard(profile="quickstart")&lt;/code&gt;, then &lt;code&gt;middleware=guard.middleware()&lt;/code&gt; on the agent.&lt;/p&gt;

&lt;p&gt;What changes after this is not your UX. It is your trust model.&lt;/p&gt;

&lt;p&gt;The input path is normalized and checked through the guard. Tool calls can be checked before execution. Memory writes can be evaluated with source and trust tags. On the allow path, the adapter stays transparent. On the block path, you get typed exceptions and structured decisions instead of vague failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle blocked paths explicitly
&lt;/h2&gt;

&lt;p&gt;Do not hide the blocked path. Model it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omega.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OmegaBlockedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OmegaToolBlockedError&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize this note and continue the workflow.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OmegaBlockedError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked model/input step&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control_outcome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason_codes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;OmegaToolBlockedError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Blocked tool call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gate_decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Omega’s integration docs make this contract explicit: blocked model or input steps raise &lt;code&gt;OmegaBlockedError&lt;/code&gt;, blocked tool calls raise &lt;code&gt;OmegaToolBlockedError&lt;/code&gt;, and the decision payload gives you control outcomes and reason codes you can route into logging or operator workflows.&lt;/p&gt;

&lt;p&gt;That is an underrated point. Good guardrails do not just stop things. They tell the rest of your application what happened in a shape the rest of your application can actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify that the integration is real
&lt;/h2&gt;

&lt;p&gt;After wiring the middleware, do not stop at “it imports.”&lt;/p&gt;

&lt;p&gt;Run the strict LangChain smoke:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/smoke_langchain_guard.py &lt;span class="nt"&gt;--strict&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Omega ships that exact smoke path for the LangChain adapter. The point is simple: prove that the guard is not just present, but actually sitting on the execution path you think it is sitting on.&lt;/p&gt;

&lt;p&gt;This is where a lot of “guardrails” fail in practice. The wrapper exists. The middleware is registered. The demo runs. But one path still bypasses the gateway, or one tool still executes outside the guard. A strict smoke is boring, and boring is good.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Omega adds beyond one-shot filtering
&lt;/h2&gt;

&lt;p&gt;The usual failure mode in these systems is isolation.&lt;/p&gt;

&lt;p&gt;A single document does not look dangerous enough. A single step does not cross the threshold. A single tool result looks routine. The problem emerges only when the system accumulates pressure across steps.&lt;/p&gt;

&lt;p&gt;Omega is built to operate on that exact shape. The docs describe the runtime as packet-based and stateful: it projects chunks into wall-pressure vectors, aggregates packet pressure, computes toxicity, accumulates session-scoped state, and then reacts when the pattern becomes strong enough. In plain English: it does not assume that every bad workflow announces itself in one obvious prompt.&lt;/p&gt;

&lt;p&gt;The same docs also spell out the default action pattern: soft-block toxic documents first, freeze tools when tool abuse participates, escalate when exfiltration participates, and treat shutdown as controlled degradation rather than a blind hard stop. That is a sane design choice for production systems, because the goal is not to make the app brittle. The goal is to make it harder to steer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start in monitor mode, not enforce mode
&lt;/h2&gt;

&lt;p&gt;The safest mistake here is not technical. It’s rollout. Don’t jump from “middleware added” straight to hard blocking.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwf8hzp2adsvo6s3wv31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwf8hzp2adsvo6s3wv31.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;A safer rollout path:&lt;/strong&gt; wire the guard, verify it is really on the execution path, observe in monitor mode, add operator workflow, then enforce.&lt;/p&gt;

&lt;p&gt;This is the part most teams skip.&lt;/p&gt;

&lt;p&gt;Omega’s own quickstart recommends a &lt;strong&gt;monitor-first validation&lt;/strong&gt; phase before enforcement. The project docs are very explicit here: run the local monitor smoke, inspect the timeline and aggregated report, confirm that risky samples produce a non-allow intended outcome, and only then move toward production hardening and enforce mode.&lt;/p&gt;

&lt;p&gt;Use this path first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python scripts/smoke_monitor_mode.py &lt;span class="nt"&gt;--profile&lt;/span&gt; dev &lt;span class="nt"&gt;--projector-mode&lt;/span&gt; pi0
omega-walls report &lt;span class="nt"&gt;--session&lt;/span&gt; monitor-smoke &lt;span class="nt"&gt;--events-path&lt;/span&gt; &amp;lt;events_path&amp;gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json
omega-walls explain &lt;span class="nt"&gt;--session&lt;/span&gt; monitor-smoke &lt;span class="nt"&gt;--events-path&lt;/span&gt; &amp;lt;events_path&amp;gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In monitor mode, the expected behavior is subtle but important: the attack sample should show &lt;code&gt;intended_action != ALLOW&lt;/code&gt;, while the &lt;code&gt;actual_action&lt;/code&gt; can still remain &lt;code&gt;ALLOW&lt;/code&gt;. That is not a bug. That is the whole point of monitor mode. It lets you validate the risk logic before you start interrupting workflows.&lt;/p&gt;

&lt;p&gt;This is the rollout path I would actually use in production:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wire the guard into LangChain.&lt;/li&gt;
&lt;li&gt;Run strict smoke locally.&lt;/li&gt;
&lt;li&gt;Enable monitor mode in a non-trivial workflow.&lt;/li&gt;
&lt;li&gt;Inspect reports and explain output.&lt;/li&gt;
&lt;li&gt;Add alerting and approvals.&lt;/li&gt;
&lt;li&gt;Move to enforcement only after operators can see and resolve the outcomes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last step matters because Omega’s docs also require alerts and approvals before production enforcement, specifically to avoid silent workflow pauses and make escalations observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging is not an afterthought
&lt;/h2&gt;

&lt;p&gt;If you are putting a trust boundary into an agent loop, logging is part of the feature, not paperwork.&lt;/p&gt;

&lt;p&gt;Omega’s logging and audit contract is built around reproducibility: an Off decision should be replayable from structured logs, using projector outputs, configuration references, and state snapshots. By default, production logging is designed to avoid storing raw content unless capture policy explicitly allows it, and the audit schema includes top contributors, actions taken, and tool-freeze state.&lt;/p&gt;

&lt;p&gt;That is the right shape for real systems. When something gets blocked, “the model acted weird” is not enough. You want to know which source pushed the workflow, what the system saw, what action it took, and whether the same event can be replayed later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not claim
&lt;/h2&gt;

&lt;p&gt;It is worth saying this plainly.&lt;/p&gt;

&lt;p&gt;Omega Walls is not a general-purpose security firewall. It does not replace infrastructure security, secret management, model-native safeguards, or moderation for direct user jailbreaks. Its guarantees depend on architecture: untrusted content has to pass through the boundary before it enters context, and tool execution has to stay behind a single gateway. If your stack bypasses those two points, the protection model breaks with it.&lt;/p&gt;

&lt;p&gt;That is not a weakness in the write-up. It is a sign that the boundary is being described honestly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;A lot of agent security writing still assumes the main problem is a bad prompt.&lt;/p&gt;

&lt;p&gt;In production, the bigger problem is usually trust confusion.&lt;/p&gt;

&lt;p&gt;Your agent reads external data. Your tools bring more external data back. Your memory carries state forward. Somewhere in that loop, normal-looking text starts behaving like control.&lt;/p&gt;

&lt;p&gt;That is why the right place to intervene is not just the prompt input. It is the boundary between untrusted content, context construction, and tool execution.&lt;/p&gt;

&lt;p&gt;If you are already running LangChain, this is a small integration. More importantly, it is the right shape of integration.&lt;/p&gt;

&lt;p&gt;Install the adapter. Wire the middleware. Run the strict smoke. Start in monitor mode. Then decide where enforcement belongs in your workflow.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/synqratech/omega-walls" rel="noopener noreferrer"&gt;https://github.com/synqratech/omega-walls&lt;/a&gt;&lt;br&gt;
PyPI: &lt;a href="https://pypi.org/project/omega-walls/" rel="noopener noreferrer"&gt;https://pypi.org/project/omega-walls/&lt;/a&gt;&lt;br&gt;
Site: &lt;a href="https://synqra.tech/omega-walls" rel="noopener noreferrer"&gt;https://synqra.tech/omega-walls&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>agents</category>
      <category>langchain</category>
    </item>
    <item>
      <title>We open-sourced Omega Walls: a stateful runtime defense for RAG and AI agents</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Tue, 14 Apr 2026 13:48:14 +0000</pubDate>
      <link>https://forem.com/anviren/we-open-sourced-omega-walls-a-stateful-runtime-defense-for-rag-and-ai-agents-3o8a</link>
      <guid>https://forem.com/anviren/we-open-sourced-omega-walls-a-stateful-runtime-defense-for-rag-and-ai-agents-3o8a</guid>
      <description>&lt;p&gt;Most prompt-injection defenses still think in single turns.&lt;/p&gt;

&lt;p&gt;But many real agent failures do not happen in one prompt. They build across retrieved documents, memory carry-over, tool outputs, and later execution.&lt;/p&gt;

&lt;p&gt;That is the problem we built &lt;strong&gt;Omega Walls&lt;/strong&gt; for.&lt;/p&gt;

&lt;p&gt;Today we’re open-sourcing Omega Walls, a Python runtime defense layer for RAG and tool-using agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Omega Walls does
&lt;/h3&gt;

&lt;p&gt;Omega Walls sits at two important runtime points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Before final context assembly&lt;/strong&gt;&lt;br&gt;
Retrieved chunks, emails, tickets, attachments, and tool outputs can be inspected before they are allowed into model context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;At tool execution&lt;/strong&gt;&lt;br&gt;
Tool calls can be constrained or blocked when accumulated risk crosses the boundary.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of treating each chunk as an isolated moderation problem, Omega Walls turns untrusted content into &lt;strong&gt;session-level risk state&lt;/strong&gt; and emits deterministic runtime actions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;block&lt;/li&gt;
&lt;li&gt;freeze&lt;/li&gt;
&lt;li&gt;quarantine&lt;/li&gt;
&lt;li&gt;attribution / reason flags&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What it is built for
&lt;/h3&gt;

&lt;p&gt;Omega Walls is designed for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;indirect prompt injection&lt;/li&gt;
&lt;li&gt;distributed attacks across multiple chunks or turns&lt;/li&gt;
&lt;li&gt;cocktail attacks that combine takeover, exfiltration, tool abuse, and evasion&lt;/li&gt;
&lt;li&gt;multi-step flows where no single step looks obviously malicious in isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why we open-sourced it
&lt;/h3&gt;

&lt;p&gt;We think agent security needs more work on &lt;strong&gt;runtime trust boundaries&lt;/strong&gt;, not only better prompt scanning.&lt;/p&gt;

&lt;p&gt;If you are building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RAG pipelines&lt;/li&gt;
&lt;li&gt;internal copilots&lt;/li&gt;
&lt;li&gt;support or inbox agents&lt;/li&gt;
&lt;li&gt;tool-using workflows&lt;/li&gt;
&lt;li&gt;agent infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we’d love your feedback.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/synqratech/omega-walls" rel="noopener noreferrer"&gt;https://github.com/synqratech/omega-walls&lt;/a&gt;&lt;br&gt;
Website: &lt;a href="https://synqra.tech/omega-walls" rel="noopener noreferrer"&gt;https://synqra.tech/omega-walls&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PyPI: &lt;a href="https://pypi.org/project/omega-walls/" rel="noopener noreferrer"&gt;https://pypi.org/project/omega-walls/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, tell us where it breaks, what attack patterns you think matter most, and where this layer should sit in a real stack.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>opensource</category>
      <category>rag</category>
      <category>security</category>
    </item>
    <item>
      <title>Why we didn’t use an LLM-first approach for architectural drift detection</title>
      <dc:creator>Anton Fedotov</dc:creator>
      <pubDate>Wed, 18 Mar 2026 08:44:29 +0000</pubDate>
      <link>https://forem.com/anviren/why-we-didnt-use-an-llm-first-approach-for-architectural-drift-detection-1ojc</link>
      <guid>https://forem.com/anviren/why-we-didnt-use-an-llm-first-approach-for-architectural-drift-detection-1ojc</guid>
      <description>&lt;h2&gt;
  
  
  Why we didn’t use an LLM-first approach for architectural drift detection
&lt;/h2&gt;

&lt;p&gt;LLMs are very good at a lot of things in software development.&lt;/p&gt;

&lt;p&gt;They can explain code, summarize pull requests, suggest fixes, and point out suspicious logic. For many review tasks, they are genuinely useful.&lt;/p&gt;

&lt;p&gt;But when we started working on architectural drift detection, we ran into a different kind of problem.&lt;/p&gt;

&lt;p&gt;Architectural drift is usually not a single “bad line of code”.&lt;br&gt;
It is a gradual shift in the shape of a codebase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;boundaries get blurred,&lt;/li&gt;
&lt;li&gt;hidden coupling appears,&lt;/li&gt;
&lt;li&gt;new state starts leaking into places that used to stay simple,&lt;/li&gt;
&lt;li&gt;control flow becomes more irregular,&lt;/li&gt;
&lt;li&gt;repo-specific patterns quietly erode over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that is where an LLM-first approach started to feel like the wrong primary layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core issue: architectural drift is not just code understanding
&lt;/h2&gt;

&lt;p&gt;A generic LLM can read code and reason about it.&lt;/p&gt;

&lt;p&gt;But architectural drift is not only about understanding what a piece of code does.&lt;br&gt;
It is about understanding whether a change is structurally abnormal &lt;strong&gt;for this repository&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;A pattern can be valid in isolation and still be a bad architectural move in a specific repo.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;introducing a new abstraction where the repo has stayed intentionally simple,&lt;/li&gt;
&lt;li&gt;adding hidden state into an area that has historically stayed stateless,&lt;/li&gt;
&lt;li&gt;crossing a module boundary that the team has treated as stable,&lt;/li&gt;
&lt;li&gt;making a PR that is locally reasonable but globally erosive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An LLM can often describe such code.&lt;br&gt;
But detecting that it is &lt;strong&gt;out of character for this codebase&lt;/strong&gt; is a different task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LLM-first review was not enough for us
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The decision is often local, but the damage is global
&lt;/h3&gt;

&lt;p&gt;Large language models are very strong at local reasoning over the code they can see.&lt;/p&gt;

&lt;p&gt;But architecture is a global property.&lt;br&gt;
A pull request can look fine line by line while still moving the whole system in the wrong direction.&lt;/p&gt;

&lt;p&gt;That is why drift often survives normal review:&lt;br&gt;
tests pass, the code works, nothing looks obviously broken — but the shape of the system worsens.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Repo-specific baselines matter more than general code knowledge
&lt;/h3&gt;

&lt;p&gt;Most AI review tools are built around broad priors learned from many repositories.&lt;/p&gt;

&lt;p&gt;That is useful for generic review.&lt;br&gt;
It is less useful for questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Is this kind of abstraction typical here?”&lt;/li&gt;
&lt;li&gt;“Does this boundary crossing fit the historical structure of this repo?”&lt;/li&gt;
&lt;li&gt;“Is this new dependency normal for this subsystem?”&lt;/li&gt;
&lt;li&gt;“Is this complexity spike expected here or is it architectural drift?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are not universal questions.&lt;br&gt;
They are baseline questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Drift detection needs stability, not just plausible reasoning
&lt;/h3&gt;

&lt;p&gt;For architecture work, noisy comments are deadly.&lt;/p&gt;

&lt;p&gt;If the system raises too many vague or unstable warnings, teams stop trusting it very quickly.&lt;/p&gt;

&lt;p&gt;We needed a layer that behaves more like structural instrumentation:&lt;br&gt;
repeatable, calibrated, and tied to measurable deviation — not just a smart narrative about code.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Explanation and detection are different jobs
&lt;/h3&gt;

&lt;p&gt;LLMs are often excellent at the second part:&lt;br&gt;
explaining why something may be risky.&lt;/p&gt;

&lt;p&gt;But the first part — consistently detecting structural deviation relative to a repo baseline — is a separate problem.&lt;/p&gt;

&lt;p&gt;We found it useful to separate those two jobs instead of forcing one model to do both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built instead
&lt;/h2&gt;

&lt;p&gt;We built a non-linguistic structural layer first.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;learn the repository’s structural baseline,&lt;/li&gt;
&lt;li&gt;compare each PR against that baseline,&lt;/li&gt;
&lt;li&gt;score the deviation,&lt;/li&gt;
&lt;li&gt;surface a short risk summary and a few hotspots directly in the PR.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our case, this became &lt;a href="https://zenodo.org/records/17820299" rel="noopener noreferrer"&gt;PhaseBrain&lt;/a&gt; inside Revieko.&lt;/p&gt;

&lt;p&gt;The model is not trying to replace LLMs.&lt;br&gt;
It is trying to do something narrower and more structural:&lt;br&gt;
track roles, boundaries, deviations, and coherence in the evolution of a repo.&lt;/p&gt;

&lt;p&gt;That gives us a better primary signal for architectural drift.&lt;/p&gt;

&lt;p&gt;Then, if needed, language models can sit on top of that signal and help explain it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our view now
&lt;/h2&gt;

&lt;p&gt;For this problem, LLMs are useful — but not as the foundation.&lt;/p&gt;

&lt;p&gt;They are strong explainers.&lt;br&gt;
They are not the best primary detector of repo-specific architectural drift.&lt;/p&gt;

&lt;p&gt;Architectural drift is less about “what does this code mean?”&lt;br&gt;
and more about&lt;br&gt;
“what does this change do to the structure of this system over time?”&lt;/p&gt;

&lt;p&gt;That pushed us toward a structural model first, and a language layer second.&lt;/p&gt;

&lt;p&gt;That is the architecture we ended up building.&lt;/p&gt;

&lt;p&gt;If you work on long-lived repos, I’d be very interested in your view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have you seen PRs that looked reasonable locally but still degraded system structure?&lt;/li&gt;
&lt;li&gt;Do you think architectural drift is better modeled as a structural signal than as a pure language task?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Revieko:&lt;br&gt;
&lt;a href="https://synqra.tech/revieko" rel="noopener noreferrer"&gt;https://synqra.tech/revieko&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>automation</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
