Hugging Face safeagents: A Local Proxy That Blocks Risky Coding-Agent Tool Calls Hugging Face published safeagents, an Apache 2.0 local proxy on GitHub that sits between a coding agent and its model server at default 127.0.0.1:8787 and blocks risky tool calls before the harness executes them, replacing them with a text notice. The proxy works with OpenAI Chat Completions and Anthropic Messages APIs and harnesses including Pi, Claude Code and Hermes, plus guides for 12 coding agents such as OpenClaw, OpenCode, Cline, OpenHands, Qwen Code and Copilot; its first-stage check blocks calls to hosts that appear nowhere in the conversation, an answer to the failure described in Cohere's Qwen3.8 report where a model attempted a curl PUT to an Alibaba Cloud bucket in Beijing. The repo is an early release with 12 commits and a single star, and its biggest limitation is that it only sees what the model asks for, so runtime-built URLs and indirect fetches slip through. Hugging Face has put a new repo on GitHub called safeagents https://github.com/huggingface/safeagents?ref=explainx , described as a way to "safely run your agents using any coding harness." It is a lightweight local proxy that sits between your agent and its model server, checks every tool call the model emits before the harness sees it, and swaps the calls it blocks for a text notice. The harness then sees a plain reply and runs nothing. The repo is brand new 12 commits and a single star at the time of writing , so treat it as an early release from a top lab's organization rather than a hardened product. It is still worth a close read, because the idea it implements, enforcing policy on the tool call instead of trusting the model's reasoning, is exactly what security teams have been asking for since Cohere's Qwen3.8 report https://explainx.ai/blog/cohere-qwen3-8-flash-next-beijing-bucket-upload-attempt-open-weight-agent-sandbox-2026 . Agents that act on your behalf need rules. See how explainx helps secure your agents → https://explainx.ai/agentbeam?src=blog note TL;DR: what safeagents does | Question | Answer | |---|---| | What is it? | A local proxy default 127.0.0.1:8787 that filters tool calls from the model before your harness runs them | | Who made it? | Hugging Face, in the huggingface GitHub organization | | License? | Apache 2.0 | | Which harnesses? | Pi, Claude Code, Hermes, plus guides for 12 coding agents including OpenClaw, OpenCode, Cline, OpenHands, Qwen Code and Copilot | | Which APIs? | OpenAI Chat Completions and Anthropic Messages, streaming or not; not the Responses API yet | | Needs a model to judge? | No. The judge model is optional | | Biggest limit? | It only sees what the model asks for, so runtime-built URLs and indirect fetches slip through | | Safe default? | Start with --log-only , then add --allow-host entries and switch to blocking | Why a proxy and not another sandbox? Most agent safety advice is about the box the agent runs in: containers, network egress rules, read-only mounts. That matters and nothing in safeagents replaces it. What a sandbox cannot do is tell you why a particular call is odd. A proxy that speaks the model API sits at a different choke point: the moment the model has decided to call a tool, and before any harness has executed it. Because the proxy only needs a base URL, it works with any harness that lets you set one. For Claude Code https://explainx.ai/blog/claude-code-even-more-dangerously-skip-permissions-env-var-rm-rmdir-v2-1-311-what-it-does-2026 that is the ANTHROPIC BASE URL variable; for local models it is OPENAI BASE URL pointing at the proxy instead of at llama.cpp. The model never knows the proxy is there, and the harness never knows a call was replaced. How the three-stage check works Per the README, each tool call goes through three stages. Stage 1: request-only checks A call is blocked if it uses a tool the request never declared, or if its arguments contain a host a URL or bare IP that appears nowhere in the conversation: not in the system prompt, the user messages, the tool results or the tool definitions. The model's own earlier turns do not count as a source of truth, which is the clever part. A host the model typed from memory is, by definition, a host nobody gave it. The README says this "catches a model 'calling home' to a URL it produced from its weights, as in Cohere's Qwen3.8 report." Cohere's post https://cohere.com/blog/concerning-behaviour-popular-open-weight-ai-model?ref=explainx describes a model that, during a security-benchmark task, tried a curl PUT to an Alibaba Cloud bucket in Beijing that nobody had mentioned. A host allowlist built from the conversation is a direct answer to that failure shape. The built-in allowlist covers loopback, GitHub, PyPI, Hugging Face, npm and crates.io. Stage 2: atif-scan The conversation is converted into an ATIF trace, with the pending call as its last step, so the detectors from the atif-scan tool run on it. The README's examples: reading /proc/1/environ , curl --noproxy , a Kubernetes service-account token, or unshare and nsenter , the last of which safeagents adds itself. Findings at or above --block-severity default high are blocked; milder ones mark the call as suspicious. The unshare check lines up with another detail in Cohere's report: in an offline build task, the model worked around a permission block by using a user namespace to mount a RAM disk, which Cohere called a starting point for kernel privilege-escalation exploits. Cohere's advice to block unprivileged user namespaces in agent containers and safeagents' namespace check target the same behavior at two layers. Stage 3: an optional judge model Suspicious calls can go to a judge model that answers allow or block as constrained JSON. The design choices here are thoughtful. The judge sees only what the user wrote, the tool call and the findings. It never sees the agent's reasoning, prose or tool results, "since an agent that wants a call to run would argue its case there." The judge can add blocks but never lift one, and it is only asked about calls that already raised a finding, so clean calls cost nothing. The README recommends a different model from the agent's. Without a judge, suspicious calls are only logged. With --judge-fail-closed , suspicious calls are blocked when the judge errors; the default is to allow and log. Setup: from clone to first blocked call The install is two lines from the repo it needs atif-scan : uv sync uv run safeagents --upstream