Memory firewall for AI agents
Your agent believes
whatever it reads
back to itself.
Agent memory is a writable, privileged input. Anything written into it is read back later as fact, so an attacker who can write to it can change what the agent believes without ever touching a prompt. Obex screens every write by the provenance of the channel it came from. Untrusted content is logged and auditable, and cannot take a belief.
pip install git+https://github.com/Pranavsingh431/nous-state
Measured, not claimed
A belief established by 4 trusted observations, then flooded with 50 false writes from a channel trusted at 0.20.
Attack success is the share of attacks where the attacker's false value becomes the belief. 400 trials per cell, seed 20260709, offline and deterministic.
Why inspecting the content fails
An attacker controls how confident their text sounds.
The obvious defense is to judge the writing. Score how reliable it reads, trust the confident ones. We measured that. Handed a confidently phrased poison, the extractor rated it 0.96 out of 1.00, and content-only trust was defeated by a single injection.
An attacker does not control which channel their text arrived on. That is the signal Obex uses, and it is the only one they cannot write.
Trust tiers are assigned by an operator in a policy file, never inferred. There is no learned trust scoring, and that is a design decision rather than a gap.
What Obex does instead
- 01
Declare where it came from
Every write names its channel. A tool, a retriever, a user session. The operator's policy file rates each one, and a channel nobody listed falls to the default, which is 0.10. A trust policy that fails open is not a trust policy.
- 02
Take the minimum over the chain
Content that crosses several surfaces carries the whole chain. The effective trust is the lowest hop, composed with content confidence by minimum. Content can only ever lower trust, never raise it.
- 03
Nothing is deleted
A held-off write stays in the log with its chain, readable in the audit trail. Obex changes what the agent believes, not what it can read back. An audit that cannot show you what it rejected is not an audit.
scraped_web0.20binds
where the attacker actually is
doc_summarizer0.90
a tool the agent trusts
Effective trust is 0.20, the lowest hop, not the summarizer's 0.90.
- Chain ignored, last hop wins
- 100.0%
- Chain honored, minimum wins
- 0.0%
Attack success, measured end to end through the engine.
Measured results
Every number here comes out of a benchmark you can run.
Laundering, through the real engine
Percent of attacks whose false value becomes the belief, measured end to end through the real engine with the rule-based extractor in the path. Lower is better.
- direct0.0%
- naive_last_hop100.0%
- taint_propagate0.0%
worst case across every injection volume tested, 100 trials per cell
Episodic memory, query-only attacker
Attack success is a poisoned trace ranking in the top k for its trigger query. The attacker never writes to the store: the agent does, on the agent's own trusted channel.
- no_enforcement100.0%
- write_channel_tiering100.0%
- session_hard_floor0.0%
- session_soft_downweight100.0%
worst case across every injection volume tested, 100 trials per cell
Drop-in integration
A LangChain memory firewall over langchain_core.chat_history.BaseChatMessageHistory. It wraps the store the agent already uses, so the transcript is untouched and only the beliefs change.
- Overhead
- 1.45 ms/write
- Coverage on free text
- 12.5%
Where it does not work
The limits, with the numbers attached.
These are measured, not hypothetical. Read them before deciding what this is worth to you.
There is a trust boundary, and we publish where it is
Attack success is zero at every volume tested while the attacker sits at or below 0.50. The breakpoint is 0.55. Against an attacker who has fully forged a channel you rate at 0.90, Obex fails at 100.0%. Reliability weighting cannot protect content it wrongly trusts.
Taint tracking stops at the memory layer
Provenance propagates through derivation chains inside Obex. It is not general information-flow control. If your code fetches a hostile page, summarizes it elsewhere, and hands you the summary as first-party input with no chain declared, Obex believes you. Declaring the chain at the boundary is integration work you have to do honestly.
Extraction understands 12.5% of free-form text
The default extractor parses narrow phrasings. On realistic agent chatter it produced claims for 1 of 8 writes. Writes it does not understand are counted and listed as unscreened, never silently passed, because a firewall that screens nothing while you believe you are covered is worse than none. LLM extraction mode is the path to closing that gap.
The cap costs accuracy when good sources sit low
A reliable source routed through a low tier gets discounted with everything else there. Accuracy falls to 8.8% at tier 0.20. The cap is free at 0.50 and above. Tier assignment quality is load bearing for utility, not only for security.
Install
Wrap the memory you already have.
The engine is open source under MIT. The PyPI release trails the repository, so install from git to get the trust policy, taint propagation and the LangChain firewall.
pip install git+https://github.com/Pranavsingh431/nous-state
from langchain_core.chat_history import InMemoryChatMessageHistory
from nous.integrations.langchain import MemoryFirewall
history = MemoryFirewall(
InMemoryChatMessageHistory(),
trust_policy="configs/trust_policy.yaml",
channel_map="configs/langchain_channels.yaml",
)
history.record("Checkout Service uses Postgres.", tool="git_log")Then read history.beliefs() for what the agent believes and history.audit() for how each belief got there.