Permission Plugin

Control which tools the model can execute through policy-based access control with blacklist/whitelist rules and interactive approval.

Name permission
Type Client-configured Plugin
Tools Provided askPermission
User Commands permissions
Auto-approved permissions

Two Distinct Roles

The permission plugin serves two independent roles:

  1. Permission Enforcement (Middleware)
    Wraps tool execution to check permissions before any tool runs. Enabled via configure_tools(permission_plugin=...)
  2. Proactive Check Tool
    Exposes askPermission for the model to query permissions. Enabled via expose_tool("permission")
Basic usage
from jaato import PluginRegistry
from shared import PermissionPlugin

# Create permission plugin
permission = PermissionPlugin()
permission.initialize({
    'channel_type': 'console',
    'policy': {
        'default': 'ask',
        'whitelist_tools': ['readFile', 'web_search'],
        'blacklist_tools': ['rm', 'sudo'],
    }
})

# Configure on client for enforcement
client.configure_tools(
    registry,
    permission_plugin=permission,
)

# Optionally expose askPermission tool
registry.expose_tool('permission')
Usage patterns
# Enforcement only (no askPermission tool)
client.configure_tools(registry, permission_plugin=permission)

# Enforcement + proactive checks
client.configure_tools(registry, permission_plugin=permission)
registry.expose_tool('permission')

# No enforcement, proactive only (not recommended)
registry.expose_tool('permission')

Configuration Parameters

config_path

Path to permissions.json configuration file.

Typestr
Default".jaato/permissions.json"

channel_type

How to request approval from the user.

Type"console" | "webhook" | "file"
Default"console"

channel_config

Configuration for the approval channel.

TypeDict[str, Any]
Default{}

policy

Inline policy dict (overrides config file).

TypeDict[str, Any]
DefaultFrom config file
Configuration example
permission = PermissionPlugin()
permission.initialize({
    'channel_type': 'console',
    'policy': {
        'default': 'ask',
        'whitelist_tools': [
            'readFile',
            'web_search',
            'getPlanStatus',
        ],
        'blacklist_tools': [
            'rm -rf *',
            'sudo *',
        ],
        'whitelist_patterns': ['read*', 'get*'],
        'blacklist_patterns': ['*delete*', '*remove*'],
    }
})
permissions.json
{
  "channel_type": "console",
  "channel_timeout": 30,
  "default": "ask",
  "whitelist_tools": ["readFile", "web_search"],
  "blacklist_tools": ["rm", "sudo"],
  "whitelist_patterns": ["read*"],
  "blacklist_patterns": ["*delete*"]
}

Policy Rules

Permission decisions are made in this order:

  1. Blacklist check: If tool matches blacklist, deny
  2. Whitelist check: If tool matches whitelist, allow
  3. Default policy: Apply default (allow/deny/ask)

Default Policies

PolicyBehavior
allowAuto-approve all unmatched tools
denyAuto-deny all unmatched tools
askPrompt channel for each unmatched tool

Pattern Matching

Whitelist/blacklist support glob patterns:

  • * matches any characters
  • ? matches single character
  • [seq] matches characters in seq
Policy examples
# Permissive: allow all, block dangerous
{
    'default': 'allow',
    'blacklist_patterns': ['*delete*', '*remove*', 'sudo*'],
}

# Restrictive: deny all, allow specific
{
    'default': 'deny',
    'whitelist_tools': ['readFile', 'web_search'],
}

# Interactive: ask for unknown
{
    'default': 'ask',
    'whitelist_tools': ['readFile'],  # Auto-approve reads
    'blacklist_tools': ['rm'],         # Auto-deny deletes
}

User Command

permissions

Manage session permissions on-the-fly. Changes persist for the current session only.

Subcommands

CommandDescription
permissions showDisplay current effective policy
permissions check <tool>Test what decision a tool would get
permissions allow <pattern>Add to session whitelist
permissions deny <pattern>Add to session blacklist
permissions default <policy>Set session default (allow/deny/ask)
permissions clearReset all session modifications
Session-Only Changes
Changes made via the permissions command only affect the current session. They don't modify the config file.
User command examples
> permissions show

Current Policy:
  Default: ask
  Whitelist: readFile, web_search
  Blacklist: rm, sudo

> permissions allow cli_based_tool

Added 'cli_based_tool' to session whitelist

> permissions default allow

Session default set to 'allow'

> permissions check updateFile

Tool: updateFile
Decision: ASK (no rule match, default policy)

> permissions clear

Session modifications cleared

Interactive Approval

When a tool requires approval (policy is "ask" or no rule match), the channel prompts the user:

Console Channel

Shows tool details and waits for keyboard input:

  • [y]es: Allow this execution
  • [n]o: Deny this execution
  • [a]lways: Allow and add to session whitelist
  • [never]: Deny and add to session blacklist

Custom Display

Plugins can provide custom formatting for their tools via format_permission_request(). For example, file_edit shows unified diffs for file modifications.

Approval prompt example
Tool: cli_based_tool
Intent: List files to find config

Arguments:
{
  "command": "ls -la /etc"
}

[y]es / [n]o / [a]lways / [never]: _
With custom display (file_edit)
Tool: updateFile
Summary: Update file: src/main.py (+5/-2 lines)

--- src/main.py (original)
+++ src/main.py (modified)
@@ -1,5 +1,8 @@
 import sys
+import os
+from pathlib import Path

 def main():
-    print("hello")
+    print("Hello, World!")

[y]es / [n]o / [a]lways / [never]: _

askPermission Tool

Allows the model to proactively check if a tool is allowed before attempting execution.

Parameters

NameTypeRequired
tool_namestringYes
intentstringYes
argumentsobjectNo

Response

FieldTypeDescription
allowedboolWhether tool is permitted
reasonstringExplanation of decision
rule_matchedstringWhich rule applied
Model using askPermission
# Model checks before executing
askPermission(
    tool_name="cli_based_tool",
    intent="Delete temporary files to free disk space",
    arguments={"command": "rm -rf /tmp/cache/*"}
)

# Response
{
  "allowed": false,
  "reason": "Tool matches blacklist pattern",
  "rule_matched": "rm*"
}
Allowed response
{
  "allowed": true,
  "reason": "Tool is whitelisted",
  "rule_matched": "readFile"
}