How to Give AI Agents Safe Access to Your Terminal
Learn to grant AI agents terminal access safely with sandboxing, permission systems, and audit trails in this practical guide.
Before you start
You have an AI agent that can run commands, but you are worried it might delete files or send secrets to the wrong place. You are not alone. Giving an AI agent raw shell access is like handing a new intern the root password: it works until it does not.
This guide shows you how to set up a safe environment for AI agents that need to execute terminal commands. You will learn three layers of protection: sandboxing, permission scoping, and audit logging. By the end, you will have a working setup that lets your agent be productive without letting it run wild.
The approach is practical. You will use Docker for isolation, a permission layer to control what the agent can do, and a logging setup to see every command it runs. All commands are copy-paste ready.
- A Linux or macOS machine with Docker installed
- Basic familiarity with the command line
- An API key for an AI model that supports tool calling (like OpenAI or Anthropic)
- Python 3.9 or later installed
The problem with raw shell access
When you give an AI agent a terminal, you give it the ability to read, write, and execute anything the user can. A single misinterpreted instruction can lead to rm -rf or a curl command that exfiltrates your environment variables.
The goal is not to remove the agent's power, but to contain it. You want the agent to be able to run commands, but only within a controlled environment. This is similar to how you would run untrusted code in a sandbox.
# Example of what NOT to do - giving full shell access
export OPENAI_API_KEY=sk-...
python agent.py --shellStep 1: Sandbox with Docker
The first layer of defense is to run the agent inside a Docker container. This isolates the agent from your host system. Even if the agent goes rogue, it cannot touch your personal files or network.
Create a Dockerfile that defines the agent's environment. Include only the tools the agent needs, like Python and curl. Do not include your host's SSH keys or cloud credentials.
FROM python:3.11-slim
# Install necessary tools
RUN apt-get update && apt-get install -y curl jq && rm -rf /var/lib/apt/lists/*
# Create a non-root user
RUN useradd -m agent
USER agent
WORKDIR /home/agent
# Copy your agent code (you will create this later)
COPY agent.py .
CMD ["python", "agent.py"]- Use a non-root user inside the container to reduce privileges.
- Do not mount your host's home directory into the container.
- Limit network access with Docker's --network flag if possible.
Step 2: Define a permission layer
Even inside a container, you may want to restrict which commands the agent can run. For example, you might allow file reads but not writes to certain directories, or allow network calls only to specific APIs.
Create a simple permission checker in Python that wraps the command execution. This checker will allowlist commands and arguments, and deny anything else.
# permission.py
import shlex
ALLOWED_COMMANDS = {
"ls": True,
"cat": True,
"pwd": True,
"curl": {"domains": ["api.example.com"]},
"python": True, # but you might restrict further
}
def check_command(command_line: str) -> bool:
parts = shlex.split(command_line)
if not parts:
return False
cmd = parts[0]
if cmd not in ALLOWED_COMMANDS:
return False
if isinstance(ALLOWED_COMMANDS[cmd], dict):
# For curl, check the domain
for arg in parts[1:]:
if arg.startswith("http"):
from urllib.parse import urlparse
domain = urlparse(arg).netloc
if domain not in ALLOWED_COMMANDS[cmd]["domains"]:
return False
return True- Start with a small allowlist and expand as needed.
- Use shlex to parse commands safely, avoiding shell injection.
- For network commands, validate domains to prevent SSRF.
Step 3: Build the agent loop
Now you will create a minimal agent that can call tools. The agent will receive a user request, decide which tool to call, execute it with the permission checker, and return the result to the model. This is a simplified version of how tools work in frameworks like LangChain, but it is transparent and easy to modify.
You will use OpenAI's function calling API for this example, but the pattern applies to any model that supports tool use.
# agent.py
import json
import openai
import subprocess
from permission import check_command
client = openai.OpenAI()
def run_command(command_line: str) -> str:
if not check_command(command_line):
return "Permission denied: command not allowed"
try:
result = subprocess.run(
command_line, shell=True, capture_output=True, text=True, timeout=10
)
return result.stdout or result.stderr
except subprocess.TimeoutExpired:
return "Command timed out"
tools = [
{
"type": "function",
"function": {
"name": "run_command",
"description": "Run a shell command in a sandboxed environment.",
"parameters": {
"type": "object",
"properties": {
"command_line": {
"type": "string",
"description": "The command to run"
}
},
"required": ["command_line"]
}
}
}
]
def agent_loop(user_input: str):
messages = [{"role": "user", "content": user_input}]
while True:
response = client.chat.completions.create(
model="gpt-4o-mini",
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:
if tool_call.function.name == "run_command":
args = json.loads(tool_call.function.arguments)
result = run_command(args["command_line"])
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result
})
else:
print(msg.content)
break
if __name__ == "__main__":
user_input = input("Ask me to do something: ")
agent_loop(user_input)Step 4: Add audit logging
You need to know what the agent did. Add logging that records every command it attempts, whether it was allowed, and the output. This is your audit trail, essential for debugging and security review.
Use Python's logging module to write to a file. In a production setup, you would send these logs to a central logging service.
# Add to agent.py
import logging
logging.basicConfig(
filename='agent_audit.log',
level=logging.INFO,
format='%(asctime)s - %(message)s'
)
def run_command(command_line: str) -> str:
if not check_command(command_line):
logging.warning(f"DENIED: {command_line}")
return "Permission denied: command not allowed"
logging.info(f"ALLOWED: {command_line}")
# ... rest of the function
result = subprocess.run(...)
logging.info(f"OUTPUT: {result.stdout[:200]}")
return result.stdout or result.stderrStep 5: Run the setup
Now you will put it all together. Build the Docker image and run the container, mounting only the directory that contains your agent code and the audit log file.
Make sure to set your OpenAI API key as an environment variable when running the container, but do not bake it into the image.
# Build the image
docker build -t safe-agent .
# Run the container with a read-only root filesystem and a volume for logs
docker run --rm -it \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-v $(pwd)/agent_audit.log:/home/agent/agent_audit.log \
--read-only \
--tmpfs /tmp \
--network none \
safe-agent- The --read-only flag prevents any writes to the container's filesystem except /tmp.
- The --network none flag disables all network access, which is the safest but limits the agent to local commands. If you need network, use a custom network with egress filtering.
- Mount the audit log file from the host so you can inspect it after the run.
Verify it worked
Test the agent with a simple request that should be allowed, and one that should be denied. For example, ask it to list files and then to delete a file.
Check the audit log to confirm the commands were logged correctly.
# Example session
$ docker run ... safe-agent
Ask me to do something: list files in current directory
# Agent runs 'ls -la' and prints output
Ask me to do something: delete all files
# Agent might try 'rm -rf *' but permission checker denies it
# Agent prints 'Permission denied: command not allowed'
# Check the audit log
cat agent_audit.logRecommended setup
For a more robust setup, consider using a tool like gVisor or Firecracker for stronger isolation, and a policy engine like OPA (Open Policy Agent) to manage permissions declaratively.
Here is a copy-paste starter for a Docker Compose file that includes the agent and a logging sidecar.
version: '3.8'
services:
agent:
build: .
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./agent_audit.log:/home/agent/agent_audit.log
read_only: true
tmpfs:
- /tmp
networks:
- no-network
networks:
no-network:
internal: trueTroubleshooting
Answers to the questions that come up most often on this topic.
- If the agent cannot run because of missing tools, install them in the Dockerfile but keep the image minimal.
- If you need network access, do not use --network none. Instead, create a network and use a proxy or firewall to restrict destinations.
- If the permission checker is too restrictive, add more commands to the allowlist, but be specific about arguments.
- If the agent loops forever, add a maximum number of tool calls per conversation.
FAQ
Answers to the questions that come up most often on this topic.
- Q: Can I use this with other AI models? Yes, the pattern works with any model that supports function calling. Adjust the API calls accordingly.
- Q: What if I need to allow the agent to write files? You can mount a specific directory as writable and adjust the permission checker to allow writes only to that directory.
- Q: How do I prevent the agent from accessing secrets? Do not pass secrets as environment variables into the container. Use a secrets manager and inject them only when needed.
- Q: Is Docker enough for security? Docker provides isolation but not a security boundary. For high-risk tasks, consider using a VM or a sandboxing tool like gVisor.
Try it on code.live
You can experiment with the permission checker logic and test your agent's commands using the JSON Diff tool to compare outputs, or use the Hash Generator to verify file integrity. These tools help you develop and debug your setup without leaving your browser.
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 how to give ai agents safe 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 September 10, 2026. Fundamentals stay stable; check linked tool pages and official docs when version-specific behavior matters.