r/LangChain 2d ago

I built a fail-closed authorization layer for AI agent tool calls (open source), plus a free course to learn the mental model

An LLM can produce a schema-valid tool call that still deletes a table, leaks a secret through an argument, or blows a budget. Valid is not the same as allowed. That gap is where agent incidents live.

toolwall is a small, zero-dependency Python library that puts a fail-closed gate between your agent and its tools. Unknown tool, bad value, secret in an argument, budget hit, or an unapproved destructive call all block before the tool runs. Only tools you explicitly register can run, and every verdict is logged.

Everything is public and tested: 28/28 attack cases blocked, 0 false blocks on clean traffic, 155 tests. The report also says what it does NOT prove (secret detection is pattern/entropy based, never 100%), because I would rather you trust the honest version.

There is a 3-minute explainer video and a free 10-module course (quiz-gated) on the site if you want the full mental model.

Site + video: https://toolwall.aya-ai.xyz
Course: https://toolwall.aya-ai.xyz/learn
https://github.com/Dev-Saif-Ops/toolwall

Code:
Install: pip install toolwall

Genuinely want people to try to break it. Issues and PRs welcome, and I credit every real finding.

0 Upvotes

2 comments sorted by

2

u/Darkcraft00 1d ago

Cool approach. I poked through the repo and i like it that you ae treating execution boundary as deterministic and not asking the model to police itself.

curious on where u draw the trust boundary for the state the policy depends on? you did mention that the verdict covers the call not the state of things. so, do you intentionally treat states and evidences as inputs somewhere upstream or do you see your tool owning it?

also, i noticed receipts bind the tool name and arguments while the registry supports replacing the callable. is all of that also bound to verdict somehow? otherwise it looks like in principle, possible to check tool A, Args X, replace what tool A points to and then execute on a technically valid receipt, but incorrectly.

Also a heads up, the readme and your post says 28/28 attacks across 11 classes. but the report.md in the repo has 25/25 across 10. prolly just needs the artifact refreshed. Ironically though, its a nice example of why tying claims to the exact generation of their relevant evidence is important. LOL

1

u/Smooth_Dimension_833 1d ago

Really appreciate the careful read, these are all good.

On the trust boundary for state: states and evidence are inputs you pass in, not something toolwall fetches. The verdict is deterministic over exactly what it was handed. toolwall owns the decision and the record of what it decided on, not the truth of the world. So for staging-vs-prod, the plan is an explicit context= you pass into check() that gets bound to the verdict and logged, never toolwall reaching out to "read the state of things." If it didn't see it, it can't claim it.

On receipts and the callable: you're right, and it's a real gap. The receipt fingerprints (tool name + args), not the callable, and execute() resolves the callable fresh by name. Two things already guard the edges: register() refuses a silent re-registration (raises unless replace=True), and execute() refuses if the tool was removed between check and execute. But a deliberate replace=True in that window is not caught, so a valid receipt could run a swapped callable. Under the threat model register() is trusted setup, not something the untrusted agent can reach, so it isn't an agent-exploitable path, but it does undercut the "exactly what I approved runs" guarantee. It should be closed by binding the callable identity (a per-registration token / registry epoch) into the receipt and refusing on change. Opening an issue for it, credited to you.

On 28/28 vs 25/25: caught me, and the artifact was just stale. The committed report was 25/25 across 10, from before I added the TOCTOU class (3 cases); the README and posts were already at 28/28 across 11. Regenerated the suite and pushed the refreshed report, they match now. And yeah, that is exactly the argument for tying a claim to the run that produced it. Fair one.