Skip to main content
The permission system controls which actions the agent can perform without asking for your approval. You can pre-approve safe actions, block dangerous ones, and always prompt for sensitive operations.

Default Permission Behavior

Devin for Terminal uses a tiered permission system to balance power and safety. The default behavior depends on the current mode:
Tool typeExampleApproval required (Normal mode)Approval required (Bypass mode)
Read-onlyFile reads, grep, globNoNo
FetchHTTP requestsYesNo
Bash commandsShell executionYesNo
File modificationEdit/write filesYesNo
In Normal mode (the default), read-only operations are auto-approved while writes and shell commands require your explicit approval. Each time you approve an action, you can choose to allow it once, for the session, or permanently for the project. In Bypass mode, all tool calls are auto-approved without prompting.
Bypass mode does not override organization-level permissions. Admin-enforced deny and ask rules configured via Team Settings remain active regardless of the user’s permission mode. See Precedence for details.

How Permissions Work

When the agent calls a tool, the permission system checks your rules in priority order:
  1. Deny rules — Checked first. If matched, the action is blocked immediately.
  2. Ask rules — Checked second. If matched, you’re always prompted (overrides any allow rules).
  3. Allow rules — Checked last. If matched, the action proceeds without prompting.
  4. Default — If no rule matches, you’re prompted for approval.
Because deny is checked before ask, and ask is checked before allow, a deny rule always wins. If the same scope matches both a deny and an ask rule, the deny takes effect.

Configuration

Add permissions to your config file’s permissions section:
// .cognition/config.json
{
  "permissions": {
    "allow": [
      "Read(src/**)",
      "Exec(npm run)"
    ],
    "deny": [
      "Exec(rm)"
    ]
  }
}

Permission Syntax

There are two types of permission matchers: scope-based (controlling what paths/commands/URLs are accessible) and tool-based (controlling which tools can be used).

Scope-Based Permissions

Read(glob)

Controls file read access. The glob pattern matches file paths.
"allow": [
  "Read(src/**)",           // All files under src/
  "Read(~/.config/**)",     // Home config files
  "Read(/tmp/**)"           // Temp directory
]
Directory paths automatically match all files within them.
Controls file write/edit access.
"allow": [
  "Write(src/**)",          // Can write anywhere in src/
  "Write(tests/**)"         // Can write test files
],
"deny": [
  "Write(*.lock)",          // Can't modify lock files
  "Write(.env*)"            // Can't modify env files
]
Controls shell command execution. Matches commands that start with the given prefix.
"allow": [
  "Exec(git)",              // git, git status, git commit...
  "Exec(npm run)",          // npm run test, npm run build...
  "Exec(python)"            // python, python script.py...
],
"deny": [
  "Exec(rm)",               // Blocks rm, rm -rf, etc.
  "Exec(sudo)"              // Blocks sudo commands
]
Exec(git) matches “git”, “git status”, “git commit -m ‘msg’” but NOT “gitk” or “github-cli”. The prefix must match as a complete word.
Controls HTTP fetch access using URL patterns.
"allow": [
  "Fetch(https://api.github.com/*)",    // GitHub API
  "Fetch(https://*.example.com/*)",     // All example.com subdomains
  "Fetch(domain:npmjs.org)"             // Any URL on npmjs.org
]
URL patterns follow the WHATWG URL Pattern standard. The domain: shorthand matches any path on the exact domain.

Tool-Based Permissions

Match by tool name to control entire tools:
{
  "permissions": {
    "deny": [
      "edit",       // Block all file edits
      "exec"        // Block all command execution
    ],
    "allow": [
      "read",       // Allow all file reads
      "grep",       // Allow all searches
      "glob"        // Allow all file finding
    ]
  }
}
Available tool names: read, edit, grep, glob, exec

MCP Tool Permissions

Control access to MCP server tools:
{
  "permissions": {
    "allow": [
      "mcp__github__list_issues",     // Specific tool on specific server
      "mcp__github__*",               // All tools on github server
      "mcp__*"                        // All MCP tools
    ],
    "deny": [
      "mcp__github__delete_repo"      // Block specific dangerous tool
    ]
  }
}
PatternMatches
mcp__server__toolOne specific tool
mcp__server__*All tools on a server
mcp__*All MCP tools everywhere

Path Patterns

Glob patterns in Read() and Write() support:
PatternMeaning
*Any characters in a single path segment
**Any characters across path segments (recursive)
~Home directory expansion
Examples:
"allow": [
  "Read(**)",                    // All files everywhere
  "Read(src/**/*.ts)",           // All TypeScript in src/
  "Write(~/projects/myapp/**)"   // Write to specific project
]

Persistence Options

When the agent asks for permission during a session, you can choose how to save your decision:
OptionWhere it’s savedShared with team?
Allow onceNot savedNo
Allow for sessionIn memory onlyNo
Allow for project.cognition/config.jsonYes
Allow for project (local).cognition/config.local.jsonNo
Allow globally~/.config/cognition/config.jsonNo

Precedence

When multiple permission sources define rules, they’re merged with this precedence (highest first):
  1. Organization/team settings (if enterprise)
  2. Session-level grants (interactive approvals)
  3. Project local config (.cognition/config.local.json)
  4. Project config (.cognition/config.json)
  5. User config (~/.config/cognition/config.json)
Organization-level denials cannot be overridden by project or user config. This ensures enterprise policies are enforced.

Examples

Minimal Development Setup

Allow common read-only operations, prompt for everything else:
{
  "permissions": {
    "allow": [
      "Read(**)",
      "Exec(git status)",
      "Exec(git diff)",
      "Exec(git log)"
    ]
  }
}

Full Trust for a Project

Auto-approve most operations within the project:
{
  "permissions": {
    "allow": [
      "Read(**)",
      "Write(src/**)",
      "Write(tests/**)",
      "Exec(npm)",
      "Exec(git)",
      "Exec(node)"
    ],
    "deny": [
      "Exec(rm -rf)",
      "Exec(sudo)",
      "Write(.env*)"
    ]
  }
}

Locked-Down Enterprise

Restrict to specific safe operations, always prompt for writes:
{
  "permissions": {
    "allow": [
      "Read(src/**)",
      "Exec(git status)",
      "Exec(git diff)",
      "Exec(npm run lint)"
    ],
    "deny": [
      "Exec(rm)",
      "Exec(sudo)",
      "Write(.env*)"
    ],
    "ask": [
      "Write(**)",
      "exec"
    ]
  }
}
In this example, writes to .env* are denied outright, all other writes always prompt the user, and only a few read-only commands are auto-approved. Since deny is checked before ask, the .env* denial takes priority over the Write(**) ask rule.