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.

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.