Security¶
aia runs with your user account's permissions. Prompt files can run shell commands and Ruby, tools can act on your system, and whatever you send goes to a model provider. This page shows where each of those happens and how to keep it under control.
Protect your API keys¶
- Keep provider keys in environment variables (
ANTHROPIC_API_KEY,OPENAI_API_KEY…), set in your shell profile or a secrets manager. aia has no config key for them, so they never need to be inaia.yml. - Never write a key into a prompt file. Prompts get shared, committed and pasted into chats.
-
MCP servers can carry credentials in your config file (
bearer_token:,headers:,env:). If yours does, make the file private: -
aia --dump FILEwrites your whole configuration, MCP credentials included. Don't share a dump without reading it. - At
debuglevel the LLM log records request details. Don't leave--debugor--log-topointed at a shared location.
What runs code¶
| Source | What it can do | Default | How to control it |
|---|---|---|---|
$(command) and $VAR in a prompt file |
Runs the command; inserts environment variables | On | shell: false in the prompt's front matter |
ERB (<%= %>, <% %>) in a prompt file |
Runs any Ruby | On | erb: false in the prompt's front matter |
Text piped to aia with no prompt ID |
Treated as a prompt, so $(...) and ERB in it run |
On | Give a prompt ID; piped text is then appended as plain context |
Executable prompts (#! files) |
Same as any prompt file | — | Read before running |
/ruby directive |
Runs any Ruby | Off | --allow-ruby-eval / flags.allow_ruby_eval |
--tools, --require, tools.paths, require_libs |
Loads Ruby code into aia | Off | Only load code you trust |
| Plugins directory | Loads every *.rb file in it, and reloads changes during chat |
Off | Only put files you trust there |
| Tools the model calls | Whatever the tool does | — | --at / --rt |
| MCP servers | A local server is a program aia starts as you; a remote one is a service you connect to | Off | Configure only servers you trust; see below |
Shell expansion and ERB in prompts¶
Prompt files are processed before they are sent: $(command) is replaced by the command's output, $NAME / ${NAME} by the environment variable (upper-case names only), and ERB is evaluated as Ruby. That's what makes prompts dynamic — and it means opening a prompt file with aia is running it.
---
description: Summarize today's commits
---
Summarize these commits for a standup:
$(git log --since=midnight --oneline)
Text inside fenced code blocks (``` or ~~~) is left alone by shell expansion. Command output is never re-expanded, and it can't inject ERB.
If a prompt only needs plain text, switch processing off in its front matter:
To switch it off for every prompt, set flags.shell: false and flags.erb: false in your config file; a prompt's own shell: / erb: front matter still wins. See ERB & Shell.
Piped text with no prompt ID is a prompt
some_command | aia (with no prompt ID) runs the piped text as a prompt, so $(...) and ERB in it are executed. Don't pipe untrusted content — a web page, an email, a downloaded file — into aia without a prompt ID. With a prompt ID (some_command | aia summarize), piped text and context files are appended as plain text and nothing in them runs.
/ruby¶
The /ruby (/rb) directive evaluates Ruby inside aia with full privileges. It is off unless you start aia with --allow-ruby-eval or set flags.allow_ruby_eval: true. When it is off, using it prints an error and does nothing. When it is on, aia prints WARNING: /ruby executing: … to stderr every time.
Enable it only for sessions where you need it, never in a shared config file.
Tools, libraries and plugins¶
Files given to --tools, gems given to --require, and files in the plugins directory are Ruby code that runs inside aia when it starts. Treat them like any program you install.
Tools are called by the model, not by you. A tool that writes files or runs commands can do so whenever the model decides to. To limit what the model can reach:
aia --chat --rq shared_tools --at read,search # only tools whose names contain these
aia --chat --rq shared_tools --rt delete,shell,exec # everything except these
Matching is by substring, case-insensitive. Run aia --list-tools (with the same options) to see what is left. Note that the automatic tool filter (-A) chooses tools by relevance; it is not a security boundary.
Tool approval¶
Tools from an MCP server marked requires_approval: true don't run until they're approved. The same rule covers an MCP server asking you for input. tools.approval (--tool-approval) decides what happens:
| Mode | Behavior |
|---|---|
ask (default) |
aia shows the tool and its arguments and asks Allow it? [y/N]. Anything but yes refuses. With no terminal (in a script or cron job), the call is refused. |
approve |
Every such call runs without asking. |
deny |
Every such call is refused. |
mcp_servers:
- name: github
command: github-mcp-server
args: ["stdio"]
requires_approval: true
tools:
approval: ask
Approval applies to every robot aia builds, including crew members and spawned specialists. It covers MCP servers marked requires_approval: and any tool whose author declared requires_approval in the tool itself. Other tools you load with --tools or --require run without asking, so restrict those with --at / --rt.
MCP servers¶
- A stdio server (
command:) is a program aia launches with your permissions. Install servers from sources you trust and pin their versions. - A remote server (
url:) receives your prompts' tool calls and whatever data they include. - Expose only the tools you need with the server's
only:/except:lists, and mark servers that can change thingsrequires_approval: true. - Use
--mcp-use NAMESto connect only the servers a session needs, or--no-mcpto connect none. aia --mcp-list --list-toolsshows exactly what each server offers before you use it.
See MCP Servers.
What leaves your machine¶
Everything in a request goes to the model provider: the prompt, roles and skills, context files, piped text, tool results and the conversation so far. If something must not leave your machine, use a local model.
A few directives send data to other services:
/webpage URLfetches the page through the pure.md service (and needsPUREMD_API_KEY)./pasteputs your clipboard into the prompt — check what's on it.
What aia writes to disk¶
| File | Contents | Control |
|---|---|---|
temp.md in the current directory |
Every response; in chat, your messages too | -o FILE, --no-output, output.file: false |
~/.prompts/_prompts.log |
Lines you type at the chat prompt | --history-file FILE, --no-history-file |
| Log files you configure | Activity; requests at debug level |
logger.*, --log-to |
If you discuss sensitive material, turn the output and history files off or point them somewhere private.
A short checklist¶
- API keys only in environment variables;
chmod 600any config file that holds tokens. - Read a prompt file, executable prompt or plugin before running one you didn't write.
- Don't pipe untrusted text into
aiawithout a prompt ID. - Leave
/rubyoff unless you need it this session. - Restrict tools with
--at/--rt; mark risky MCP serversrequires_approval: true. - Use
--tool-approval denyfor unattended runs that shouldn't change anything. - Use a local model for data that must stay on your machine.