Obex is a memory firewall for AI agents. Agent memory is a writable, privileged input: anything that gets written into it later gets read back 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 arrived on, so content from an untrusted source is logged and auditable but cannot take a belief.
It is built on nous-state, the open-source belief engine, and on the paper arXiv:2606.22030.
A belief established by 4 trusted observations, flooded with 50 false writes from a channel trusted at 0.20, 400 trials per cell, seed 20260709:
| memory | attack success |
|---|---|
| last write wins | 100.0% |
| majority vote | 100.0% |
| Obex, provenance capped | 0.0% |
Attack success is the percentage of attacks where the attacker's false value becomes the agent's belief. Lower is better.
Every number in this repository, in the console, and on the site comes from
public/results.json and public/memory_stream.json, which are serialized directly from the
benchmarks in nous-state. Nothing is typed in by hand.
npm install
npm run devThen open http://localhost:3000.
npm run build # production build, no Python required
npm startThe JSON contracts are committed to public/, so the build has no Python step and no network
dependency on the research repository.
The site renders committed contracts. If you want to regenerate them and check the numbers yourself, you need the research repository, because the builders import both the engine and the benchmark scripts, and the benchmark scripts are research code rather than an installed package.
git clone https://github.com/Pranavsingh431/nous-state
pip install -e ./nous-state
NOUS_STATE_REPO=./nous-state python harness/build_results.py
NOUS_STATE_REPO=./nous-state python harness/build_memory_stream.py
npm run syncharness/requirements.txt pins the engine as a git dependency, because the taint propagation,
trust policy, LangChain firewall and episodic store these builders use are ahead of the current
PyPI release of nous-state.
build_results.py carries a drift guard: after building the contract it re-runs each
benchmark's own main(), parses the printed tables, and fails loudly if any cell disagrees. The
site cannot show a number that has drifted from the benchmark that produced it.
Benchmarks live in nous-state under benchmark/:
poison_bench.py, poison_launder_bench.py, mincap_utility_bench.py, taint_engine_bench.py,
policy_bench.py, langchain_wrapper_bench.py, episodic_poison_bench.py.
These are measured, not hypothetical. Read them before deciding what Obex is worth to you.
The defense has a trust boundary at roughly 0.55. Attack success is 0 percent at every injection volume tested while the attacker's channel is trusted at or below 0.50, measured on the 4 value configuration with 4 trusted observations. The breakpoint is 0.55. Above it the defense degrades, and against an attacker who has fully forged a trusted channel at 0.90 it fails completely, at 100.0 percent attack success. Reliability weighting cannot protect content it wrongly trusts. If your attacker can compromise a channel you rate highly, Obex does not save you.
Taint tracking is scoped to the memory layer. Provenance propagates through derivation chains inside the Obex pipeline, so content that entered on an untrusted channel keeps its origin tier no matter how many trusted intermediaries re-observe it. Measured end to end: routed through a trusted intermediary that forgets the untrusted origin, attack success returns to 100.0 percent; with propagation it is 0.0 percent. This is not general information-flow control. If your code fetches an attacker-controlled page, summarizes it elsewhere, and hands the summary to Obex 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 coverage on free-form text is 12.5 percent. The default rule-based extractor parses narrow phrasings. On structured phrasing it understands 100 percent of writes. On realistic agent chatter it produced claims for 1 of 8 writes. Writes that yield no claims are recorded as unscreened events, counted, and listed in the screening report, never silently passed, because a firewall that screens nothing while you believe you are covered is worse than no firewall. LLM extraction mode handles arbitrary text and is the path to closing that gap. It is nondeterministic and needs an API key, so it is excluded from the seeded benchmarks.
The provenance cap costs benign accuracy at low tiers. A genuinely reliable source routed through a low tier gets discounted along with everything else there. Accuracy falls to 53.8 percent in the reliability conflict regime and 8.8 percent in the stable noise regime when the reliable source sits at tier 0.20. The cap is free at tier 0.50 and above. Tier assignment quality is load bearing for utility, not only for security.
Trust tiers are assigned by an operator, never inferred. There is no learned or automatic trust scoring, and that is a design decision rather than a gap. A content-derived trust signal is the attack surface: the extractor rated a confidently phrased poison at 0.96 reliability, and content-only trust was defeated by a single injection at 100 percent attack success.
Episodic memory results assume a reduced threat model. The episodic numbers model a query-only attacker whose poisoned traces are written by the trusted agent, later retrieved as demonstrations, reduced to the retrieval layer. This is not a MINJA reproduction, and MINJA's published success rates are not comparable to anything measured here.
app/ Next.js App Router, the console and its routes
components/ UI, charts, the design tokens in app/globals.css
lib/ contract types and the build time loaders
public/ the committed JSON contracts the site renders
harness/ the contract builders, for regeneration only
scripts/sync-data.mjs copies regenerated contracts into public/, manual
MIT. See LICENSE.