Declarative Permissions
Pre-declare allow/deny rules in YAML, CLI, or Python for CI-safe agent runs
from praisonaiagents import Agent
agent = Agent(name="ci-agent", instructions="Run safely with pre-declared tool permissions.")
agent.start("Run tests and deploy to staging.")Declarative permissions let you pre-declare which tool calls to allow, deny, or ask about — so unattended CLI/CI runs stay safe without interactive prompts.
The user starts an unattended run; declarative rules resolve each tool call to allow, deny, or ask.
Quick Start
- YAML
yaml # agents.yaml permissions: "read:*": allow "bash:rm *": deny "*": ask agents: assistant: role: Assistant instructions: Help safelybash praisonai run agents.yaml - CLI flags
bash praisonai run "deploy the app" \ --allow 'read:*' \ --allow 'bash:git *' \ --deny 'bash:rm *' \ --permission-default ask - Python
python from praisonaiagents import Agent agent = Agent( name="CI Worker", instructions="Run safely in CI", approval={ "permissions": { "read:*": "allow", "bash:rm *": "deny", }, }, ) - In project config
Declare rules directly in
.praisonai/config.yaml— automatically inherited by everypraisonai runin the directory:yaml # .praisonai/config.yaml permissions: default: ask rules: - { pattern: "bash:git *", action: allow } - { pattern: "bash:rm *", action: deny }Or use the flat mapping form:
yaml # .praisonai/config.yaml permissions: "read:*": allow "bash:rm *": deny "*": ask
How It Works
On each tool call, the backend builds a target string (tool_name:arg) and PermissionManager.check() returns allow, deny, or ask. Non-interactive runs honour declared rules; ask without a human present falls back to deny.
Pattern Syntax
Patterns use <tool_name>:<arg-glob>:
| Pattern | Meaning |
|---|---|
read:* | Any read tool call (does not authorise secret files — see below) |
read:.env / read:.pem / read:id_rsa | Secret-specific reads — required to opt in to reading credential files |
bash:git * | Git shell commands |
write:/etc/* | Writes under /etc/ (including truncating redirections) |
tool:delete_* | MCP-namespaced tool names — matched against the bare or tool:<name> form |
external_dir:/data/* | Allow access to /data/ when workspace_root is set — automatic on Agent-built managers since PR #5305 |
external_dir:~/* | Allow access to home directory when workspace_root is set — automatic on Agent-built managers since PR #5305 |
external_dir:* | Deny all out-of-workspace access (hard block, no ask prompt) |
external_dir: targets
external_dir:<parent>/* is emitted for any path that resolves outside workspace_root. Use these patterns to pre-authorise or hard-block external filesystem access without interactive prompts. Since PR #5305, the Agent auto-sets workspace_root=os.getcwd() when it builds a manager from permissions=, so these rules are active out of the box. See Workspace Boundary.
Compound shell commands
bash: and shell: targets are decomposed into their constituent operations — &&, ;, |, subshells, $(...), backticks, and truncating redirections. A single deny rule fires when the blocked executable appears anywhere inside the compound command. For example, deny: bash:rm * blocks cd /tmp && rm -rf x and echo $(rm -rf x), not just a bare rm call.
Full behaviour details, evasion table, and aggregation rules
Detailed dict form supports is_regex, priority, agent_name, and description:
permissions:
"read:*": allow
"bash:rm *":
action: deny
description: Block destructive shell ops
priority: 100Configuration Surfaces
| Surface | Where | Example |
|---|---|---|
| Per-agent definition | mode: / permission: in .praisonai/agents/*.{md,yaml} | mode: read-only |
| Project config | .praisonai/config.yaml permissions: | "bash:rm *": deny |
| YAML | Top-level or per-agent approval.permissions: in agents.yaml | "bash:rm *": deny |
| CLI | --allow, --deny, --permissions <file>, --permission-default | --deny 'bash:rm *' |
| Python | ApprovalConfig(permissions={...}) | {"read:*": "allow"} |
Precedence: agent definition mode: / permission: → agent-level approval.permissions → top-level agents.yaml permissions: → .praisonai/config.yaml permissions: → CLI --allow/--deny (override file) → --permission-default → built-in defaults. CLI flags override config-yaml rules per-pattern. See Agent Presets & Modes for the full per-agent permission reference.
Load from file:
praisonai run agents.yaml --permissions permissions.yaml --deny 'bash:curl *'Common Patterns
Read-only CI runner — --permission-default deny with --allow 'read:*'.
Git-only worker — allow bash:git *, deny everything else.
Deny destructive commands — baseline "bash:rm ": deny and "write:/etc/": deny.
Lock down external filesystem access — add "external_dir:*": deny when workspace_root is set for a hard block on everything outside the project.
Opt in to reading .env in a trusted CI — add a secret-specific allow rule, not a broad read:*:
permissions:
"read:*": allow # ordinary files auto-read
"read:*.env": allow # explicit opt-in for .env — the broad rule alone won'tBest Practices
Set permission-default deny in CI
Use --permission-default deny or "*": deny so undeclared tools are blocked.
Prefer specific patterns first
Higher-priority rules win — declare bash:git push before broad bash:.
Version-control policy files
Keep permissions.yaml next to agents.yaml in git.
Test with –no-tty locally
Verify rules before deploying to CI.
Related
Model + MCP + permissions in one config file
Command-Aware PermissionsHow compound commands are decomposed and checked
PermissionsProgrammatic PermissionManager API
Workspace BoundaryGate shell and file access outside your project root
ApprovalInteractive approval backends
Tool Approval CLICLI flags reference