DEV Community

brian austin
brian austin

Posted on

Claude Code permissions: --dangerously-skip-permissions vs allowedTools explained

Claude Code permissions: --dangerously-skip-permissions vs allowedTools explained

Every Claude Code user hits this moment: you're mid-session, Claude asks "can I run this command?" and you just want it to stop asking.

You have two options, and they work very differently.

The two permission systems

# Option 1: Skip all permission checks (session-level)
claude --dangerously-skip-permissions

# Option 2: Pre-approve specific tools (config-level)
# In settings.json:
{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git *)",
      "Write(src/**)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

What --dangerously-skip-permissions actually does

This flag disables the entire human-in-the-loop confirmation system for that session.

Claude can:

  • Run any bash command without asking
  • Write to any file without asking
  • Execute any tool without asking

The name is honest. It is dangerous. But it's also how you run Claude Code in headless/automated mode.

When it's appropriate:

  • CI pipelines where there's no human to confirm
  • Sandboxed Docker containers with no production access
  • Local dev environments where you trust the codebase

When it's not appropriate:

  • Any environment with production credentials
  • Projects with sensitive files
  • When you haven't audited the CLAUDE.md instructions

What allowedTools does instead

allowedTools in settings.json lets you pre-approve specific tool patterns. Claude asks about everything else.

{
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run lint)",
      "Bash(git status)",
      "Bash(git diff)",
      "Read(**)",
      "Write(src/**)",
      "Write(tests/**)"
    ],
    "deny": [
      "Bash(rm *)",
      "Bash(curl *)",
      "Bash(wget *)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This is the surgical approach. Claude runs pre-approved commands freely, asks about anything unexpected, and is blocked from destructive commands.

The glob patterns for allowedTools

The pattern syntax matters:

Bash(npm *)      → any npm command
Bash(git *)      → any git command
Write(src/**)    → any file under src/
Write(*.md)      → any markdown file in current dir
Read(**)         → read any file
Enter fullscreen mode Exit fullscreen mode

Practical config for a Node.js project:

{
  "permissions": {
    "allow": [
      "Bash(npm *)",
      "Bash(node *)",
      "Bash(git status)",
      "Bash(git diff)",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(cat *)",
      "Bash(ls *)",
      "Read(**)",
      "Write(src/**)",
      "Write(tests/**)",
      "Write(*.md)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(curl *)",
      "Bash(wget *)",
      "Bash(pip *)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Project-level vs user-level permissions

You can set permissions at two levels:

User-level (~/.claude/settings.json):

  • Applies to all projects
  • Good for always-safe commands like git status, ls, cat

Project-level (.claude/settings.json in your repo):

  • Applies only to this project
  • Good for project-specific build commands
  • Committed to git so the team shares the same config
# Check what's currently allowed
cat ~/.claude/settings.json
cat .claude/settings.json  # project-level
Enter fullscreen mode Exit fullscreen mode

The hook alternative

For more complex permission logic, hooks give you programmatic control:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 /home/user/check-command.py"
          }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Your check-command.py receives the tool input and can approve, block, or modify the command before it runs. This is how you build custom security policies.

Rate limits and permission fatigue

One underrated cost of the confirmation system: when Claude is frequently pausing to ask permission, it breaks the session flow. Each pause interrupts the reasoning chain.

For local development, a pre-approved allowedTools config eliminates 90% of interruptions while keeping dangerous operations gated.

If you're hitting rate limits on top of permission fatigue — Claude Code pausing both to ask permission AND hitting Anthropic's API limits — the underlying infrastructure is working against you.

Setting ANTHROPIC_BASE_URL to a rate-limit-free proxy (simplylouie.com runs one at $2/month) removes the API limit side of the equation, so the only pauses left are the ones you explicitly kept in your permissions config.

Quick reference

Approach Scope Risk Use case
--dangerously-skip-permissions Session High CI, sandboxed containers
allowedTools in settings.json Permanent Low Daily dev workflow
Hooks with custom script Per-tool Configurable Complex security policies
No config (default) All tools ask None Sensitive/unfamiliar codebases

The default (ask for everything) is correct when you're in an unfamiliar codebase. allowedTools is correct for your regular projects. --dangerously-skip-permissions is only correct when you've explicitly sandboxed the environment.


Using Claude Code with rate limits? simplylouie.com runs a Claude proxy at $2/month — set ANTHROPIC_BASE_URL and the overloaded errors stop.

Top comments (0)