Skip to main content
Configuration and security

Permissions

Learn Qoder CLI CN's permission modes, rule configuration, and the tool-call approval flow.

Permission modes

Permission modes determine how Qoder handles tool calls — each mode sits differently on the two axes of "degree of automation" and "security laxity."
ModeBest forBehavior
defaultRegular interactive useSafe reads and internal actions run automatically; sensitive operations request confirmation.
accept_editsEveryday coding tasksAuto-approves safe file edits within the working directory. Shell commands, external actions, and sensitive paths still go through normal checks.
autoAutomated runs, unattended Goal executionZero prompts. Safe reads and in-workspace edits are auto-approved; risky actions are denied or judged by the AI classifier.
bypass_permissions (YOLO)Trusted local experiments onlySkips approval prompts. A small set of path-shape protections stays active — see Path Shapes.
dont_askHeadless flows that must never promptNever asks. Any action that would require asking is denied.

Cycling through modes

In an interactive session, press Shift+Tab to cycle through all permission modes. Press Ctrl+Y to jump straight to YOLO mode.

Startup flags

Use --permission-mode to choose the default behavior for the current session:
qodercn --permission-mode default
qodercn --permission-mode accept_edits
qodercn --permission-mode plan           # kept for backward compatibility; translates to default + entering the Plan working state
qodercn --permission-mode auto
qodercn --permission-mode bypass_permissions
qodercn --permission-mode yolo           # equivalent to bypass_permissions
qodercn --permission-mode dont_ask

# Shortcuts
qodercn --yolo                         # equivalent to --permission-mode bypass_permissions
qodercn --dangerously-skip-permissions # same as above
In an interactive session, you can also switch to YOLO mode quickly with Ctrl+Y. Multiple naming formats are supported (case-insensitive):
Canonical value (snake_case)camelCaseAliases
accept_editsacceptEdits—
bypass_permissionsbypassPermissionsyolo, YOLO
dont_askdontAsk—
Example:
# The following are equivalent
qodercn --permission-mode bypass_permissions
qodercn --permission-mode bypassPermissions
qodercn --permission-mode yolo
qodercn --yolo
qodercn --dangerously-skip-permissions
Non-default modes only take effect in trusted directories. If the current directory is not trusted, Qoder falls back to default.

How permissions are decided

Qoder performs a permission check before every tool call. There are only three permission outcomes:
  • allow: execute the tool immediately.
  • ask: external confirmation is required before execution.
  • deny: block the tool call.
Permissions apply to file reads and edits, Bash commands, web fetching, MCP tools, Subagents, and other built-in tools.

Evaluation order

Qoder evaluates permissions in a fixed order:
  1. deny rules first — a hit means denial.
  2. The tool's own safety checks (such as dangerous-command detection and sensitive-path detection).
  3. ask rules — a hit marks the call as requiring confirmation.
  4. Tool-level allow rules and the automatic allowances the mode brings.
  5. If the final result is still ask, the runtime environment decides how to consume it.
A broad allow rule does not mean all actions run silently — safety checks and ask rules take higher precedence.

How ask is consumed in different runtime environments

RuntimeWhere ask goesNotes
TUI (interactive terminal)Confirmation promptThe user chooses allow/deny in the terminal.
Headless (-p/--prompt)Auto-deniedNo one to interact; ask becomes deny.
SDK (stdio protocol)Sends a canUseTool callback to the hostThe host program decides allow/deny.
ACP (IDE integration)Sends a requestPermission RPC to the IDEThe IDE prompts or decides automatically.
In headless mode (-p/--prompt), pair with permission modes:
# Headless + accept_edits: file edits auto-approved, Bash denied
qodercn -p "refactor the utils module" --permission-mode accept_edits

# Headless + bypass_permissions: everything allowed (trusted scenarios only)
qodercn -p "run the migration" --yolo

# Headless + precise allow rules: only specific tools allowed
qodercn -p "check status" --allowed-tools 'Read,Bash(git status)'

Permission configuration

Configuration sources (8-layer precedence)

Rules merge from multiple sources, from lowest to highest precedence:
LayerSourceDescription
1userSettings~/.qoder-cn/settings.json (user global)
2projectSettings<project>/.qoder/settings.json (project level, team shared)
3localSettings<project>/.qoder/settings.local.json (this machine, add to .gitignore)
4flagSettingsThe file specified by the --settings <path> CLI flag
5cliArgThe --allowed-tools / --disallowed-tools command-line flags
6commandThe /allow and /deny in-session commands
7sessionRuntime temporary rules ("allow for this session" in the prompt)
Rules from higher-precedence sources override lower ones. If the organization policy enables allowManagedPermissionRulesOnly, only policy-managed rules are used. How to configure each source:
  • Layers 1-3 (settings files): write permissions.allow / permissions.deny / permissions.ask arrays in the JSON file at the corresponding path. settings.local.json suits per-machine personal approval rules; add it to .gitignore.
  • Layer 4 (flagSettings): qodercn --settings ./custom-settings.json specifies an extra settings file path. Its format is the same as a standard settings.json.
  • Layer 5 (cliArg): configure via command-line flags such as --permission-mode, --allowed-tools, --disallowed-tools, and --tools, effective for the current session only.
  • Layer 6 (command): type /allow Bash(npm test) or /deny WebFetch in a session; persisted to settings.local.json.
  • Layer 7 (session): temporary rules produced by choosing "allow for this session" in the prompt; gone when the process exits.

Mode configuration

Setting the default mode — configure general.defaultPermissionMode in settings:
{
  "general": {
    "defaultPermissionMode": "accept_edits"
  }
}
Supported values:
ValueBehaviorAliases
defaultPrompt for confirmation on each operation—
accept_editsAuto-approve file edits; shell commands still require confirmationacceptEdits
planRead-only mode (backward compatible; actually becomes default + the Plan working state)—
autoThe AI classifier assesses operation safety—
bypass_permissionsSkip permission prompts (YOLO)yolo, bypassPermissions
dont_askNon-interactive; operations requiring confirmation are denied outrightdontAsk
Case-insensitive; all aliases work. Disabling YOLO mode — organization admins can block users from entering bypass_permissions mode with:
{
  "security": {
    "disableYoloMode": true
  }
}
Once set, --yolo, --permission-mode bypass_permissions, and Ctrl+Y are all unavailable, and the Shift+Tab cycle skips the mode. A subagent declaring bypass is also downgraded to acceptEdits. Disabling Plan mode — if you don't need the Plan workflow, turn it off:
{
  "general": {
    "plan": {
      "enabled": false
    }
  }
}
Once set, the /plan command is unavailable, --permission-mode plan falls back to default, and the EnterPlanMode/ExitPlanMode tools are not registered. Auto-mode classifier configuration — you can steer the AI classifier's leanings with natural-language rules:
{
  "autoMode": {
    "allow": [
      "running npm/yarn/pnpm scripts defined in package.json",
      "creating or editing test files"
    ],
    "soft_deny": [
      "deleting files outside the test directory",
      "modifying CI/CD configuration"
    ],
    "environment": [
      "This is a Node.js monorepo with pnpm workspaces",
      "The project uses Vitest for testing"
    ]
  }
}
FieldPurpose
allowDescriptions of operations the classifier should lean toward auto-approving
soft_denyDescriptions of operations the classifier should lean toward denying
environmentEnvironmental context provided to the classifier
These rules are soft steering — injected into the classifier prompt as reference, with the final decision still made by the AI classifier. For security, autoMode configuration is read only from trusted sources (user global settings and localSettings); project settings are excluded to prevent malicious privilege escalation.

Permission rules configuration

Rules are grouped into allow, ask, and deny:
{
  "permissions": {
    "allow": [
      "Read(/src/**)",
      "Edit(/src/**)",
      "Bash(npm run test:*)"
    ],
    "ask": [
      "Bash(npm publish:*)",
      "WebFetch"
    ],
    "deny": [
      "Read(*.pem)",
      "Bash(rm -rf:*)"
    ]
  }
}

Rule syntax

FormMeaning
ToolNameApplies to the entire tool.
ToolName(content)Applies to a path, command, agent type, or other specific content the tool supports.
*Matches all tools.
Use canonical tool names: Read, Edit, Write, Bash, Grep, Glob, WebFetch, WebSearch, Agent, plus MCP tool names like mcp__github__create_issue. If the content contains parentheses, escape them:
{
  "permissions": {
    "allow": [
      "Bash(python -c \"print\\(1\\)\")"
    ]
  }
}
ToolName(*) is equivalent to ToolName (a tool-level rule).

Command-line overrides

qodercn --allowed-tools 'Read,Grep,Bash(git status)'
qodercn --disallowed-tools 'Bash(rm -rf:*),mcp__github__delete_repo'
qodercn --tools 'Read,Grep,Edit'
--allowed-tools and --disallowed-tools use the same rule syntax as settings. --tools restricts the set of built-in tools available for this run (unlisted tools are denied).

Trusted directories

Qoder treats the current working directory (CWD) at startup as the primary trusted directory. Within a trusted directory:
  • File reads are allowed by default
  • File writes can be auto-approved in accept_edits and auto modes
  • Non-default permission modes (auto, bypass, etc.) can take effect
If the current directory is not trusted, Qoder forcibly falls back to default mode.

Extending the trust scope

Add extra trusted working directories via --add-dir, the /add-dir command, or permissions.additionalDirectories:
qodercn --add-dir ../shared
{
  "permissions": {
    "additionalDirectories": ["../shared"]
  }
}
You can also configure permissions.trustDirectories in the global settings to permanently trust frequently used directories.

Protected paths

Some paths are protected because editing them could change execution behavior, credentials, or tool behavior. Examples include .git, .vscode, .idea, .husky, most .qoder configuration files, shell startup files like .bashrc/.zshrc, Git configuration, .mcp.json, and .ripgreprc. In regular interactive modes these paths require explicit approval; in auto mode they are denied.

Path Shapes

Some path spellings can reach resources outside the local filesystem, or bypass path comparison. These are handled separately from rules, on two independent axes.
  • Network locations — paths beginning with two separators (\server\share, //server/share), extended UNC forms (\?\UNC\server\share), WebDAV spellings, and /net/<host>/… automount paths. On Windows, reading, searching, writing and shell access there ask for confirmation every time. The prompt offers no persistent rule, because a saved rule would permanently authorize disclosing your credentials to that host. A network location may be the working directory or an added directory; startup names it in a warning.
  • Local mounts — \wsl$\…, \wsl.localhost\…, \?\C:\… and \?\Volume{…}\… name local storage and follow the normal rules. A loopback host such as \127.0.0.1\share is still a network location.
  • Bypass-prone spellings — alternate data streams (file.txt:stream), 8.3 short names (PROGRA~1), device paths (\.\…), trailing dots or spaces, three or more consecutive dots, and reserved device names (CON, NUL, COM1) require explicit approval.

File access rules

Path-scoped read rules use Read(...). Path-scoped write rules use Edit(...); they cover the file-write checks of Edit, Write, and NotebookEdit. An Edit(...) allow rule on a path also implicitly allows reading the same path. File rules use gitignore-style matching.
PatternMeaning
/src/**A path based on the rule source's root. In project/local settings, relative to the project root; in user settings, relative to the home directory.
~/Documents/**A path based on the home directory.
//tmp/data/**An absolute path from the system /. Absolute paths require a double slash. In a rule pattern a leading // means "absolute from the filesystem root" and is unrelated to the network-location handling above.
*.secretA rootless filename pattern that can match anywhere.
Example:
{
  "permissions": {
    "allow": [
      "Read(/src/**)",
      "Edit(/src/**)",
      "Read(~/Documents/specs/**)"
    ],
    "ask": [
      "Edit(/package.json)",
      "Read(~/Downloads/**)"
    ],
    "deny": [
      "Read(*.pem)",
      "Edit(/.git/**)",
      "Edit(//etc/**)"
    ]
  }
}

Bash rules

Bash(...) rules can match exact commands, command prefixes, or wildcard patterns.
RuleWhat it matches
Bash(npm run build)Exactly npm run build.
Bash(npm run test:*)npm run test and commands starting with npm run test .
Bash(git log *)Glob-style wildcard matching.
Bash(git status)Exactly git status.
Example:
{
  "permissions": {
    "allow": [
      "Bash(git status)",
      "Bash(git log:*)",
      "Bash(npm run test:*)"
    ],
    "ask": [
      "Bash(npm publish:*)",
      "Bash(git push:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(sudo:*)"
    ]
  }
}
Shell matching is conservative:
  • deny and ask rules see through common wrappers and environment-variable prefixes, so Bash(rm -rf:*) still intercepts wrapped destructive commands.
  • Prefix and wildcard allow rules do not silently approve compound commands unless every top-level command segment can be allowed independently.
  • Some provably read-only shell commands can be auto-allowed after deny, ask, and path checks.
  • Dangerous commands (such as destructive deletions or force pushes) may force confirmation even under a broad allow rule. In auto mode, dangerous shell commands are denied.
Unless you fully trust the current session, avoid broad rules like Bash or Bash(*).

Web and MCP rules

Web tools can be controlled with tool-level rules. Use ask if all web fetches should require confirmation; use deny if a session or project should have network access disabled.
{
  "permissions": {
    "ask": [
      "WebFetch"
    ],
    "deny": [
      "WebSearch"
    ]
  }
}
MCP tools use fully qualified names:
mcp__<server>__<tool>
Supported MCP patterns include:
RuleMeaning
mcp__github__create_issueA single MCP tool.
mcp__github__*All tools under the github MCP server.
mcp__githubAll tools under the github MCP server.
mcp__*All MCP tools.
Example:
{
  "permissions": {
    "allow": [
      "mcp__context7__*"
    ],
    "ask": [
      "mcp__github__create_issue"
    ]
  }
}
MCP server configuration can also set automatic allowance for a server's tools via alwaysAllow. To enable only specific MCP servers for a run, use --allowed-mcp-server-names.
qodercn --allowed-mcp-server-names context7,github

Subagent rules

Agent rules control Subagent launches. The rule content is a Subagent name, matched case-insensitively. All three behaviors are supported and evaluated in the order deny > ask > allow. When no matching rule is configured, the launch proceeds without a confirmation prompt.
RuleMeaning
AgentApplies to every Subagent launch.
Agent(general-purpose)Applies to one Subagent name, case-insensitively.
Example:
{
  "permissions": {
    "allow": [
      "Agent(explore)"
    ],
    "ask": [
      "Agent(deploy-helper)"
    ],
    "deny": [
      "Agent(code-review)"
    ]
  }
}
  • A matching deny rule blocks the launch.
  • A matching ask rule shows a confirmation prompt before the Subagent starts.
  • A matching allow rule explicitly records the approval; the outcome is the same as having no rule.
Use /agents to view the built-in Subagent list and your installed Subagents, whose names are the values usable in Agent(...) rules.

Hooks and permissions

Qoder's Hook system has two injection points in the permission decision chain, letting custom scripts influence tool allow/deny behavior.

Hook events that affect permissions

Hook eventWhen it firesPossible permission impact
PreToolUseBefore tool execution (permission check stage)Can return `permissionDecision: "allow"
PermissionRequestAfter the permission pipeline produces ask, before the promptCan return decision.behavior of allow or deny, replacing user interaction
Other Hook events (PostToolUse, SessionStart, Stop, etc.) do not participate in permission decisions.

PreToolUse Hook

Fires before tool execution. The hook script can inspect the tool name and arguments and return a permission decision:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python ./scripts/check-bash-command.py"
          }
        ]
      }
    ]
  }
}
The hook script receives JSON input via stdin (including tool_name, tool_input, session_id, etc.) and outputs a JSON result via stdout:
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Command blocked by security policy"
  }
}
Possible permissionDecision values:
  • "allow": skip the permission pipeline and approve directly
  • "deny": skip the permission pipeline and deny directly
  • "ask": continue through the normal permission pipeline (default behavior)

PermissionRequest Hook

Fires after the permission pipeline produces ask and before the prompt/callback. Suited to automated approval systems or external notifications (like Slack/email alerts):
{
  "hooks": {
    "PermissionRequest": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node ./scripts/auto-approve-safe-ops.js"
          }
        ]
      }
    ]
  }
}
Output format:
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {},
      "updatedPermissions": []
    }
  }
}

Hook vs. permission mode precedence

A hook's permission decision takes precedence over the permission mode — even in bypass_permissions mode, a PreToolUse Hook returning deny still blocks execution. This provides organizations with non-bypassable enforcement for security policies. Execution order:
  1. Hook PreToolUse → short-circuits if it returns allow/deny
  2. Permission pipeline (rules + mode + safety checks)
  3. If the result is ask → Hook PermissionRequest → short-circuits if it returns allow/deny
  4. Finally the runtime environment consumes the ask (prompt/deny/callback)