Sandbox your Agno agent with IronClaw¶
You built an agent with Agno (formerly Phidata): a model, a persona, and a
Toolkit of functions the model can call. That is a great way to design behavior.
The problem starts when it runs somewhere real: an Agno agent runs its tools in
your own process, with your API key in memory and unrestricted outbound network.
One prompt-injected instruction and a ShellTools call is a shell on your box.
IronClaw runs the same job behind a sealed sandbox instead: no network card, the model key held host-side and never in the agent, and every privileged tool call routed through a human-approval gateway and an audit log.
Runnable example
A one-command Agno-to-IronClaw example lives at
examples/integrations/agno:
a real agno.tools.Toolkit whose sandboxed_shell tool executes inside an
IronClaw sandbox, driven over one benign task plus a battery of escape attempts,
each reported blocked. Zero credentials, just Docker.
Why sandbox this¶
A typical Agno agent hands the model a host-executing toolkit:
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.shell import ShellTools
agent = Agent(
model=OpenAIChat(id="gpt-4o", api_key="sk-..."), # key lives in this process
tools=[ShellTools()], # ShellTools runs on YOUR box
)
agent.print_response("summarize today's incidents")
Three things are true of that agent, and all three are risks:
- The key is in the process. Anything that can read
os.environcan readsk-.... - Tools run with your privileges.
ShellToolsexecutes on your host, with your filesystem and network. - Egress is wide open. The process can reach any host on the internet.
IronClaw closes all three by construction. The example keeps your agent exactly as designed and swaps the toolkit for a sandboxed one.
See it work first (no credentials)¶
git clone https://github.com/IronSecCo/ironclaw.git && cd ironclaw
examples/integrations/agno/run.sh
It brings up the offline mock provider (no model key), builds a real Agno
Toolkit, and drives its sandboxed_shell tool over one benign task plus a battery
of escape attempts — network exfil, host-filesystem read, Docker-socket takeover —
each reported BLOCKED. The run exits non-zero if any containment expectation
fails, so it doubles as a CI check.
The fix, in your agent¶
Keep the agent; swap the toolkit. The example's IronClawTools is an ordinary
agno.tools.Toolkit, so it drops into Agent(tools=[...]) with no other change:
from ironclaw_sandbox import IronClawSandbox
from ironclaw_tool import IronClawTools
from agno.agent import Agent
with IronClawSandbox() as sandbox:
agent = Agent(tools=[IronClawTools(sandbox)]) # sandboxed_shell runs inside the box
Agno turns the toolkit method's type hints and docstring into the LLM tool schema,
exactly as it does for any toolkit; the command just lands inside a sealed sandbox as
an unprivileged uid, with network=none and no host mounts.
What you gained¶
- The key left the agent. It lives host-side and is injected per request; a compromised agent has nothing to steal.
network=noneby default. The sandbox has no NIC. The only egress is the audited model-proxy socket, plus whatever hosts you explicitly allowlist.- Privileged actions are gated. Registering a tool, spawning another agent, or reaching a new host flows through a human-approval gateway and the audit log.
Same agent you designed in Agno. A perimeter it never had.