What Happens When You Give an AI Agent Access to Your Terminal?
Learn the risks and rewards of giving AI agents terminal access with a practical sandbox setup and safety guardrails.
Before you start
You have seen the demos: an AI agent that reads your repo, runs tests, and opens pull requests. The pitch is that it saves you hours of boilerplate. But when that agent has access to your shell, it can also delete files, push code, or exfiltrate secrets. The question is not whether you should do it, but how to do it safely.
In this article, I will walk through a concrete setup that lets an AI agent execute commands in a sandboxed environment. You will see the minimal code to define a tool, the permission model that keeps it in check, and the observability you need to know what it did. By the end, you will have a safe playground to experiment with.
Step 1: Define the tool interface
The first step is to define what the agent can do. You do not give it a raw shell. Instead, you expose a curated set of tools, each with a name, description, and input schema. This is the standard function-calling pattern used by most agent frameworks.
Here is a minimal example in Python using a simple JSON-based tool definition. This is the contract your agent will follow.
import json
def run_command(command: str) -> str:
# This function will be implemented in the next step
return "output"
TOOLS = [
{
"type": "function",
"function": {
"name": "run_command",
"description": "Execute a shell command and return its output",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The command to execute"
}
},
"required": ["command"]
}
}
}
]
# Example tool call from the agent
call = {"name": "run_command", "arguments": {"command": "ls -la"}}
print(json.dumps(call))Step 2: Implement a safe executor
The executor is where you enforce safety. You will run commands inside a Docker container with no network access, a read-only filesystem except for a temporary work directory, and a strict time limit. This contains the blast radius.
Here is a Python implementation that uses Docker to run a command safely. It mounts a temporary directory, sets a CPU and memory limit, and kills the container after a timeout.
import docker
import os
import tempfile
def run_command_safe(command: str, timeout: int = 30) -> str:
client = docker.from_env()
with tempfile.TemporaryDirectory() as tmpdir:
container = client.containers.run(
image="python:3.12-slim",
command=["sh", "-c", command],
detach=True,
working_dir="/workspace",
volumes={tmpdir: {"/workspace": "rw"}},
network_disabled=True,
mem_limit="512m",
cpu_period=100000,
cpu_quota=50000, # 0.5 CPU
read_only=True,
tmpfs={"/tmp": "rw,noexec,nosuid,size=64m"},
)
try:
result = container.wait(timeout=timeout)
output = container.logs(stdout=True, stderr=True).decode()
return f"Exit code: {result['StatusCode']}\n{output}"
finally:
container.remove(force=True)
# Example usage
print(run_command_safe("echo hello && pwd"))- Network is disabled, so the agent cannot exfiltrate data.
- Filesystem is read-only except the temporary workspace.
- Memory and CPU limits prevent resource exhaustion.
- Timeout kills runaway processes.
- The container is removed after each run.
Step 3: Build the agent loop
Now you need the loop that connects the agent model to your tools. The agent receives a user request, calls the tool, gets the result, and repeats until it produces a final answer. This is the core of any agentic system.
Below is a minimal loop using the OpenAI API and the tools you defined. It sends the conversation history and the tool definitions, processes any tool calls, and returns the final response.
import json
from openai import OpenAI
client = OpenAI() # assumes OPENAI_API_KEY is set
TOOLS = [...] # from Step 1
def agent_loop(user_message: str, max_steps: int = 5) -> str:
messages = [{"role": "user", "content": user_message}]
for _ in range(max_steps):
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=TOOLS,
tool_choice="auto",
)
msg = response.choices[0].message
messages.append(msg)
if msg.tool_calls:
for tool_call in msg.tool_calls:
args = json.loads(tool_call.function.arguments)
result = run_command_safe(args["command"])
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
else:
return msg.content
return "Max steps reached"
# Example
print(agent_loop("List the files in the current directory"))Step 4: Add permissions and approvals
Even with a sandbox, you may want to require human approval for certain commands. For example, you might allow read-only commands automatically but require approval for anything that writes or deletes. This is a common pattern in production agent systems.
Here is a simple permission checker that categorizes commands and blocks or flags them. You can integrate this into your executor before running the command.
import re
ALLOWED_READ_ONLY = re.compile(r"^(ls|cat|pwd|echo|git status|git diff)")
BLOCKED = re.compile(r"^(rm|mkfs|dd|shutdown|reboot)")
def check_permission(command: str) -> str:
if BLOCKED.match(command):
return "blocked"
if ALLOWED_READ_ONLY.match(command):
return "auto"
return "require_approval"
def run_command_with_permission(command: str) -> str:
level = check_permission(command)
if level == "blocked":
return "Command blocked by policy"
if level == "require_approval":
# In a real system, prompt the user here
print(f"Approval needed for: {command}")
# Simulate approval
return run_command_safe(command)
return run_command_safe(command)
# Test
print(run_command_with_permission("ls -la"))- Use a whitelist for read-only commands to avoid friction.
- Use a blacklist for destructive commands that should never run.
- For everything else, require interactive approval.
- Log every command for audit.
Step 5: Observe and log everything
You cannot trust an agent you cannot observe. Log every tool call, the command, the output, and the duration. This gives you a trail for debugging and security review.
Here is a simple logging wrapper that records to a JSON file. In production you would send this to a centralized logging system.
import json
import time
from datetime import datetime
def log_tool_call(name: str, arguments: dict, result: str, duration: float):
entry = {
"timestamp": datetime.utcnow().isoformat(),
"tool": name,
"arguments": arguments,
"result": result[:500], # truncate long outputs
"duration_seconds": duration,
}
with open("agent_log.jsonl", "a") as f:
f.write(json.dumps(entry) + "\n")
def run_command_logged(command: str) -> str:
start = time.time()
result = run_command_with_permission(command)
duration = time.time() - start
log_tool_call("run_command", {"command": command}, result, duration)
return resultRecommended setup: full sandbox config
Here is a copy-paste Docker Compose setup that gives you a disposable sandbox for testing AI agents. It includes a container with Python and Node, no network, and a volume for the workspace. Use this as your starting point.
version: "3.8"
services:
agent-sandbox:
image: ubuntu:22.04
container_name: agent-sandbox
working_dir: /workspace
volumes:
- ./workspace:/workspace
network_mode: "none"
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid,size=64m
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpu_count: 1
command: sleep infinity- Replace the image with python:3.12-slim or node:20 if you need those runtimes.
- Mount a host directory as the workspace so you can share files.
- Keep network_mode none unless the agent absolutely needs network access.
- Always use read_only and tmpfs for writable temp space.
- Use security_opt no-new-privileges to prevent privilege escalation.
What I would do
If you are serious about giving an AI agent terminal access, start with a dedicated virtual machine or container that you can destroy and recreate. Do not run it on your daily driver. Use the permission checker and logging from the start, even if it feels like overhead.
Here is a minimal bash script that sets up a fresh sandbox and runs the agent with your tool definitions. Save it as run_agent.sh and make it executable.
#!/bin/bash
set -euo pipefail
docker compose up -d
# Run your agent loop inside the sandbox
docker exec -it agent-sandbox python /workspace/agent.py
docker compose downTroubleshooting
If the agent fails to run a command, check the Docker container logs. Common issues include missing packages, permission denied on the workspace, or the container being killed due to memory limits.
If the agent hangs, increase the timeout in the executor or reduce the complexity of the task. Also verify that the tool schema matches what the model expects.
- Use docker logs agent-sandbox to see container output.
- Check that the workspace directory exists and has write permissions.
- If commands need network, temporarily enable it but be aware of the risks.
- Test with a simple command like echo hello before running complex tasks.
FAQ
Answers to the questions that come up most often on this topic.
- Is it safe to give an AI agent terminal access? It can be safe if you sandbox the environment, restrict permissions, and log all actions. Never run it on a production system without these safeguards.
- What if the agent tries to run a destructive command? Your permission checker should block or require approval for commands like rm -rf. The sandbox also limits the damage.
- Can I use this with any AI model? The function-calling pattern works with most major models, including OpenAI, Anthropic, and open-source models via frameworks like LangChain.
- How do I prevent the agent from accessing the internet? Set network_mode none in Docker or use a firewall. This prevents data exfiltration.
- Do I need to write my own agent loop? You can use frameworks like LangChain or AutoGPT, but writing a minimal loop helps you understand the safety boundaries.
Key takeaways
- Apply one concrete change from this post before collecting more reading.
- Prefer browser-side tools when the work involves secrets, tokens, or PII.
- Document the why next to the how so the next reviewer inherits context.
FAQ
- Who is this guide on ai-agents for?
- Working developers who need a practical take on what happens when you give an ai agent access to your terminal? — not a marketing overview. Skim the sections, apply one tip, then come back when you hit an edge case.
- Do I need an account to use the related tools?
- No. code.live tools run in your browser with no signup. Nothing you paste is uploaded to a server for the client-side utilities linked from this post.
- How often is this article updated?
- This post was published August 23, 2026. Fundamentals stay stable; check linked tool pages and official docs when version-specific behavior matters.