Skip to main content
Set a workspace root and any tool call touching a path outside it asks for extra approval — a single broad allow no longer lets the agent reach your whole machine.
This feature is introduced in PraisonAI PR #2575. The workspace_root parameter is set on PermissionManager directly. When workspace_root=None (the default), no boundary check runs and existing behaviour is unchanged.

Quick Start

1

Enable workspace boundary

Create a PermissionManager with workspace_root and the boundary gate activates automatically:
from praisonaiagents.permissions import PermissionManager

manager = PermissionManager(workspace_root="/path/to/project")

result = manager.check("bash:cat /etc/passwd")
print(result.needs_approval)  # True — /etc/passwd is outside workspace
print(result.target)          # bash:cat /etc/passwd
When the manager checks cat /etc/passwd, it returns needs_approval=True — even if a broad bash:cat * → allow rule is set.
2

Pre-authorise an external directory

Add a rule to allow a specific external directory without prompting:
from praisonaiagents.permissions import PermissionManager, PermissionRule, PermissionAction

manager = PermissionManager(workspace_root="/proj")
manager.add_rule(PermissionRule(
    pattern="external_dir:/data/*",
    action=PermissionAction.ALLOW,
    description="Allow shared data volume",
))

result = manager.check("bash:cat /data/input.csv")
print(result.action)  # allow — pre-authorised

How It Works

StepAction
1Tool call arrives as bash:<cmd> or file tool target
2CommandParser extracts path-like arguments from the command
3Each path is checked with path_safety.is_within_root(path, workspace_root)
4Out-of-workspace paths emit an external_dir:<parent>/* sub-target
5Sub-target evaluated — defaults to ask unless a rule pre-authorises or denies it

What Triggers the Gate

Any path that resolves outside workspace_root triggers an external_dir: approval request.
Path formExampleOutcome
Absolute path/etc/hostsexternal_dir:/etc/* → ask
Home-relative~/.bashrcexternal_dir:<home>/* → ask
Explicit relative../../etc/passwdexternal_dir:/etc/* → ask
Env-prefixed$HOME/.config/xexternal_dir:<home>/* → ask
Bare traversalsub/../../etc/passwdexternal_dir:/etc/* → ask
Joined flag value--config=/etc/xexternal_dir:/etc/* → ask
Short-getopt attachedcurl -o/tmp/fileexternal_dir:/tmp/* → ask
Redirect write-targetecho x > ~/.bashrcexternal_dir:<home>/* → ask
External executable/tmp/tool.shexternal_dir:/tmp/* → ask
Bare PATH-resolved command names like ls, rm, cat are not boundary-checked — only tokens that reference the filesystem by path. An executable named /tmp/tool.sh is checked; a bare ls is not.

Aggregation with Existing Rules

The workspace boundary fits into the same deny→ask→allow aggregation as all other permission checks.
Rule stateCommand touches external pathOutcome
bash:cat * → allowcat /etc/passwdask (boundary gate fires)
external_dir:/data/* → allowcat /data/x.csvallow (pre-authorised)
external_dir:* → denycat /etc/passwddeny (hard block)
bash:rm * → denyrm -rf /etc/otherdeny (explicit deny wins first)
Deny always wins — an explicit deny on bash:rm * or external_dir:* beats the boundary ask, and an explicit deny cannot be overridden by a boundary allow.

Backward Compatibility

When workspace_root is None (the default), no boundary check runs and behaviour is 100% unchanged. Only opting in to workspace_root activates the gate — no existing code breaks.
from praisonaiagents.permissions import PermissionManager

# No workspace_root → old behaviour, no boundary check
manager = PermissionManager()

# With workspace_root → boundary gate active
manager_bounded = PermissionManager(workspace_root="/proj")

Fail-Closed Behaviour

If path_safety cannot be imported, or if a path cannot be resolved (e.g. broken symlink, permission error), the boundary check fails closed — it emits an external_dir: → ask rather than silently allowing the operation. This is intentional and tested behaviour. Do not report it as a bug. If you see unexpected ask prompts in edge-case environments, check whether path resolution is failing.

Pre-Authorising External Directories

praisonai permissions allow "external_dir:/data/*" --description "Shared data volume"
praisonai permissions deny "external_dir:*" --description "Block all external paths"

Best Practices

Point workspace_root at your checked-out repository path. The agent can freely read and write within the project; anything outside requires explicit pre-authorisation.
If your workflow legitimately reads from /data/ or /shared/, add external_dir:/data/* → allow. This is safer than using --approve-all-tools which bypasses the gate entirely.
Set external_dir:* → deny to hard-block all out-of-workspace access. No ask prompt appears — the operation is immediately rejected. Use this in high-security CI pipelines.
A bash:rm * → deny rule fires before the boundary check. You can still hard-block destructive commands regardless of whether the path is inside or outside the workspace.

Permissions Module

Programmatic PermissionManager API and configuration options

Command-Aware Permissions

How compound shell commands are decomposed and checked

Declarative Permissions

YAML, CLI, and Python permission policies

Interactive Approval

User-facing approval prompts and backends