Permissions
Control what tools can do with a flexible permission system. Protect sensitive operations while allowing the model to work freely on safe tasks.
Why Permissions?
AI models are powerful but unpredictable. Without guardrails, a model might:
- Delete important files
- Execute destructive commands
- Access sensitive data
- Make unintended API calls
The permission system lets you allow helpful actions while blocking dangerous ones—with user approval as a middle ground.
Permission Policies
Policies define the rules for each tool or pattern of tools. Each policy specifies one of three actions:
| Policy | Behavior |
|---|---|
allow |
Execute immediately, no questions |
deny |
Block execution, return error to model |
ask |
Prompt user for approval each time |
Policy Matching
Policies can match:
- Exact tool names —
execute_command - Wildcards —
file_*matches all file tools - All tools —
*as default
More specific patterns take precedence over wildcards.
from shared.plugins.permission import (
create_plugin as create_permission,
PermissionPolicy
)
# Create permission plugin
perm = create_permission()
# Define policies
policies = [
# Safe tools: always allow
PermissionPolicy(
pattern="read_file",
action="allow"
),
PermissionPolicy(
pattern="list_directory",
action="allow"
),
# Dangerous: always deny
PermissionPolicy(
pattern="execute_command",
action="deny"
),
# File writes: ask user
PermissionPolicy(
pattern="write_file",
action="ask"
),
PermissionPolicy(
pattern="delete_file",
action="ask"
),
# Default: ask for unknown tools
PermissionPolicy(
pattern="*",
action="ask"
)
]
perm.initialize({"policies": policies})
Configuration
Permission policies can be configured via code or a JSON file.
Config File
For easier management, define policies in a JSON file:
Configuration Options
| Option | Description |
|---|---|
policies |
List of permission policies |
default_action |
Action for unmatched tools (ask) |
ask_timeout |
Seconds to wait for user response |
remember_session |
Remember user choices this session |
{
"default_action": "ask",
"ask_timeout": 60,
"remember_session": true,
"policies": [
{
"pattern": "read_*",
"action": "allow",
"description": "Reading is safe"
},
{
"pattern": "list_*",
"action": "allow"
},
{
"pattern": "search_*",
"action": "allow"
},
{
"pattern": "write_*",
"action": "ask",
"description": "Confirm before writing"
},
{
"pattern": "delete_*",
"action": "ask",
"description": "Confirm before deleting"
},
{
"pattern": "execute_command",
"action": "deny",
"description": "Shell commands blocked"
}
]
}
from shared.plugins.permission import create_plugin
perm = create_plugin()
perm.initialize({
"config_path": "permissions.json"
})
# Use with client
client.configure_tools(
registry,
permission_plugin=perm
)
Using with Client
Pass the permission plugin to configure_tools() to
enable permission checking for all tool calls.
How It Works
- Model decides to call a tool
- Permission plugin checks the policy
- Based on policy:
allow: Tool executesdeny: Error returned to modelask: User prompted
- Result returned to model
User Prompts
When a tool requires approval, the user sees the tool name and arguments, and can approve or deny.
from jaato import JaatoClient, PluginRegistry
from shared.plugins.permission import (
create_plugin as create_permission,
PermissionPolicy
)
# Create client
client = JaatoClient()
client.connect() # Reads JAATO_PROVIDER and MODEL_NAME from env
# Create registry and expose tools
registry = PluginRegistry(model_name=client.model_name)
registry.discover()
registry.expose_tool("cli")
registry.expose_tool("file_edit")
# Create permission plugin
perm = create_permission()
perm.initialize({
"policies": [
PermissionPolicy("read_file", "allow"),
PermissionPolicy("write_file", "ask"),
PermissionPolicy("execute_command", "ask"),
PermissionPolicy("*", "ask"),
],
"remember_session": True
})
# Configure with permissions
client.configure_tools(
registry,
permission_plugin=perm
)
# Now tool calls go through permission check
response = client.send_message(
"Delete the temp files",
on_output=handler
)
# User will be prompted before delete executes
Auto-Approved Tools
Plugins can declare certain tools as "auto-approved"—these bypass permission checks entirely. Use for tools that are inherently safe.
When to Use
- Read-only operations
- Local calculations
- Status queries
- Tools with no side effects
class MyPlugin:
def get_tool_schemas(self):
return [
ToolSchema(name="get_status", ...),
ToolSchema(name="calculate", ...),
ToolSchema(name="write_file", ...),
]
def get_auto_approved_tools(self):
"""
These tools skip permission checks.
"""
return [
"get_status", # Read-only
"calculate", # No side effects
# NOT write_file - has side effects
]
# See all auto-approved tools
registry.discover()
registry.expose_tool("my_plugin")
plugin = registry.get_plugin("my_plugin")
auto = plugin.get_auto_approved_tools()
print(f"Auto-approved: {auto}")
# These will execute without permission check
# even if policy says "ask" or "deny"
Ask Channels
When a tool needs user approval, the permission plugin uses an "ask channel" to communicate with the user.
Built-in Channels
| Channel | Description |
|---|---|
console |
Prompt in terminal (default) |
queue |
Queue requests for async handling |
auto_deny |
Deny all without asking |
auto_allow |
Allow all without asking |
Queue Channel
The queue channel is useful for GUI apps or async workflows.
Permission requests are queued and can be processed separately from
the main execution flow.
# Console channel prompts in terminal
perm.initialize({
"policies": policies,
"ask_channel": "console"
})
# User sees:
# Allow tool "write_file" with args {"path": "config.json"}?
# [y/n]:
# Queue channel for async/GUI apps
perm.initialize({
"policies": policies,
"ask_channel": "queue"
})
# Permission requests are queued
# Your app processes them asynchronously
# Get pending requests
pending = perm.get_pending_requests()
for req in pending:
print(f"Tool: {req.tool_name}")
print(f"Args: {req.args}")
# Process in your UI, then respond
perm.respond_to_request(
req.request_id,
approved=True # or False
)
# Auto-allow all (useful for testing)
perm.initialize({
"policies": policies,
"ask_channel": "auto_allow"
})
# Auto-deny all (safe default)
perm.initialize({
"policies": policies,
"ask_channel": "auto_deny"
})
Session Memory
With remember_session=True, user choices are remembered
for the current session. This avoids asking repeatedly for the same
tool.
How It Works
- User approves
write_file - Future
write_filecalls auto-approve - Memory clears when session ends
Granularity
Memory can be per-tool or per-tool-with-args:
per_tool: Approve once for all callsper_call: Approve each unique call
perm.initialize({
"policies": policies,
# Remember approvals this session
"remember_session": True,
# Granularity: "per_tool" or "per_call"
"remember_granularity": "per_tool",
})
# With per_tool:
# - User approves write_file once
# - All write_file calls auto-approve
# With per_call:
# - User approves write_file("a.txt")
# - write_file("a.txt") auto-approves
# - write_file("b.txt") still asks
# Clear all remembered approvals
perm.clear_session_memory()
# Check current memory
memory = perm.get_session_memory()
for tool, approved in memory.items():
status = "approved" if approved else "denied"
print(f"{tool}: {status}")
Best Practices
Default to Ask
Use ask as the default action. Only allow
tools you've verified are safe.
Be Specific
Use specific patterns rather than broad wildcards. read_file
is better than *_file.
Document Policies
Add descriptions to policies so users understand why they're being asked.
Review Regularly
As you add tools, review your permission policies to ensure new tools are covered appropriately.
{
"default_action": "ask",
"remember_session": true,
"remember_granularity": "per_tool",
"ask_timeout": 30,
"policies": [
// Explicitly safe - allow
{"pattern": "read_file", "action": "allow"},
{"pattern": "list_directory", "action": "allow"},
{"pattern": "search_files", "action": "allow"},
{"pattern": "get_*", "action": "allow"},
// Destructive - ask with description
{
"pattern": "write_file",
"action": "ask",
"description": "This will modify a file"
},
{
"pattern": "delete_*",
"action": "ask",
"description": "This will delete data"
},
// Very dangerous - deny
{
"pattern": "execute_command",
"action": "deny",
"description": "Shell commands are blocked"
},
{
"pattern": "*_production_*",
"action": "deny",
"description": "Production access blocked"
}
]
}
Next Steps
- Tools — Understand tool execution
- Building Plugins — Create plugins with auto-approved tools
- Permission Plugin Reference — Full API details
# Policy actions
"allow" # Execute immediately
"deny" # Block with error
"ask" # Prompt user
# Pattern matching
"tool_name" # Exact match
"read_*" # Wildcard suffix
"*_file" # Wildcard prefix
"*" # Match all (default)
# Ask channels
"console" # Terminal prompt
"queue" # Async queue for GUI apps
"auto_deny" # Deny all asks
"auto_allow" # Allow all asks
# Memory
"per_tool" # Remember by tool name
"per_call" # Remember by tool + args