Claude Code Permissions on a Remote VM: Auto Mode vs --dangerously-skip-permissions
Three options for handling Claude Code's approval gates: disable them entirely with --dangerously-skip-permissions, auto-approve specific tools, or handle them remotely from your phone with Grass.
TL;DR
Running Claude Code unattended on a remote VM means choosing between three permission strategies: skip all checks with --dangerously-skip-permissions, pre-approve specific commands via settings.json, or use Anthropic's newer Auto Mode to let the agent self-approve low-risk tools. Each trades security for convenience differently. If you want genuine oversight without being blocked, Grass lets you approve or deny tool executions from your phone in real time — no pre-configuration required. Start with codeongrass.com for a free 10-hour trial.
Why permission handling gets complicated on a headless VPS
On your laptop, Claude Code prompts you in the terminal every time it wants to run a bash command, write a file, or call an external API. That workflow breaks the moment you SSH into a VPS, kick off an agent, and close your laptop lid. The agent hits a permission prompt, waits for input that never comes, and either stalls or errors out.
There are three ways to solve this:
- Skip all permission checks entirely (
--dangerously-skip-permissions) - Pre-approve specific tools via an allowlist in
settings.json - Use Claude Code's Auto Mode to let the model self-approve low-risk actions
A fourth option — remote approval from your phone — is covered in the Grass section below.
What does --dangerously-skip-permissions actually do?
The flag tells Claude Code to suppress every interactive permission prompt and allow all tool use unconditionally. The agent will run bash commands, read and write files, make network requests, and install packages without asking.
claude --dangerously-skip-permissions -p "Refactor the auth module and run the test suite"
In a tmux or screen session on your VPS, this lets the agent run indefinitely without input. That is the appeal.
The risk is exactly what the name says. The agent can:
- Execute
rm -rfon paths it misidentifies - Write credentials to disk if it hallucinates a config path
- Install packages that introduce vulnerabilities
- Make destructive git operations (force pushes, branch deletions)
A detailed breakdown of the real-world risks covers several categories of damage that are hard to undo. The Medium post from April 2026 makes the same point more bluntly: using the flag as a default productivity shortcut is how you eventually wipe a database or commit secrets.
When it's actually appropriate:
- Isolated, ephemeral containers with no persistent state
- Sandboxed environments with no network access to production
- Throwaway VMs that get destroyed after the task
If you're running the agent on a VPS that has SSH keys, production credentials, or a mounted filesystem with real data, skip-permissions is the wrong default.
How to pre-approve specific tools with settings.json
Claude Code reads a settings file that lets you whitelist specific tools and bash command patterns. This gives you unattended operation without blanket permission bypass.
The file lives at ~/.claude/settings.json (user-level) or .claude/settings.json in the project root.
{
"permissions": {
"allow": [
"Bash(npm run test)",
"Bash(npm run build)",
"Bash(git status)",
"Bash(git diff*)",
"Bash(git add*)",
"Bash(git commit*)",
"Bash(python -m pytest*)",
"Read(*)",
"Write(src/**)",
"Write(tests/**)"
],
"deny": [
"Bash(rm -rf*)",
"Bash(curl*)",
"Bash(wget*)",
"Bash(npm install*)"
]
}
}
The Bash(...) entries use glob-style prefix matching. git diff* matches git diff, git diff --staged, git diff HEAD~1, and so on.
Start the agent normally without the skip-permissions flag:
claude -p "Run the test suite, fix any failures, and commit the result"
When the agent requests a tool that matches the allowlist, it proceeds silently. When it hits something not on the list, it still prompts — which on a headless VPS means it blocks. The solution is to think carefully about what your task requires and pre-populate the allowlist for that class of work.
For a full-featured development loop, your allowlist typically needs to cover: reading any file, writing to project directories, running your test command, running your build command, and basic git operations. Be explicit about what you deny — it forces you to audit the blast radius.
Troubleshooting tip: If the agent keeps getting blocked on a tool you thought you allowed, check the exact command string. Bash(git commit -m*) will not match Bash(git commit --amend). Use broader globs or add both variants explicitly.
What is Claude Code Auto Mode and how is it different?
Auto Mode is Anthropic's answer to permission fatigue. Instead of you pre-configuring an allowlist, the model itself decides whether a tool execution is low-risk enough to self-approve. High-risk actions (anything matching patterns like destructive file operations or network requests to unknown hosts) still surface for human approval.
A recent Reddit thread captures the developer response well: the constant permission prompts for mundane operations like reading a file or running tests were the most complained-about friction point in Claude Code's agentic workflow, and Auto Mode addresses that directly.
To enable Auto Mode, set it in your settings:
{
"autoApprove": true
}
Or pass it at invocation:
claude --auto-approve -p "Implement the feature described in SPEC.md"
The difference from --dangerously-skip-permissions:
--dangerously-skip-permissions |
Auto Mode | Allowlist (settings.json) |
|
|---|---|---|---|
| Who decides? | Nothing — all approved | Model | You |
| Risk surface | Everything | Model's judgment | Only what you listed |
| Configuration required | None | None | Yes |
| Unattended safe? | Only in sandbox | Mostly | Yes, if list is tight |
| Blocks on unknown tools? | Never | Sometimes | Yes |
For a high-level overview of how these three strategies stack up for unattended VPS deployments, compare Auto Mode vs --dangerously-skip-permissions for unattended VPS agents.
Auto Mode is the pragmatic middle ground for most development tasks. The model generally makes conservative choices. It will self-approve ls, cat, git status, and test runners. It will pause on curl to an unknown domain, rm with broad patterns, or anything touching credentials.
The remaining risk: the model's judgment is not perfect. It can misclassify a risky operation as benign. For production-adjacent work, the allowlist approach gives you more deterministic behavior. For isolated development tasks — scaffolding a feature, writing tests, refactoring — Auto Mode is the right default.
Permission handling for Codex CLI
OpenAI's Codex CLI uses a config.toml file for command approval. The relevant section:
[sandbox]
# Commands the agent can run without asking
allowed_commands = [
"npm test",
"npm run build",
"python -m pytest",
"git status",
"git diff",
"git add",
"git commit",
]
# Prefix-match rules — 'git *' allows any git subcommand
allowed_prefixes = [
"git ",
"python -m pytest ",
]
For unattended headless operation, Codex also supports a --full-auto flag that bypasses the interactive approval loop entirely. It carries the same risk profile as --dangerously-skip-permissions in Claude Code — use it only in disposable environments.
codex --full-auto "Refactor the database layer to use connection pooling"
The allowlist approach via config.toml is the safer path for long-running Codex sessions on a VPS.
Permission handling for OpenCode
OpenCode's permission model is flag-parity with Claude Code. The relevant flags:
# Skip all permissions (equivalent to --dangerously-skip-permissions)
opencode run --skip-permissions "your prompt here"
# Auto-approve low-risk tools (similar to Claude Code Auto Mode)
opencode run --auto-approve "your prompt here"
OpenCode reads the same ~/.claude/settings.json format when running Claude Code as its underlying model, so your allowlist configuration carries over without changes.
What if you want oversight without being blocked?
The fundamental tension: pre-approving tools gives you unattended operation but removes your ability to catch unexpected behavior in the moment. Skip-permissions removes all friction but also removes all control. Auto Mode helps but still relies on the model's self-assessment.
None of these options give you real-time visibility into what the agent is doing when you're away from your desk.
This is where Grass's permission forwarding changes the equation. Instead of choosing between "approve everything in advance" or "skip all checks," Grass forwards each tool execution request — bash commands, file writes, anything the agent asks to do — to your phone as a native modal. You see the exact command, approve or deny it, and the agent continues or stops accordingly.
This works because Grass maintains a persistent session on a cloud VM. The agent keeps running; you're the remote approver. If you're in a meeting and the agent wants to run npm install some-package, you get a push notification, see the full command, and tap approve or deny. If you're asleep and the agent wants to drop a table, it waits for your response.
The practical workflow:
- Push your task to Grass from the mobile app or CLI
- The agent starts on a pre-configured Daytona VM
- As the agent works, permission requests arrive on your phone
- You approve routine operations and deny anything unexpected
- The session survives disconnects — reconnect picks up exactly where you left off
This is not a theoretical capability layered on top of the agent — it's built into how Grass runs sessions. You get unattended operation with on-demand oversight instead of an all-or-nothing tradeoff.
Grass supports Claude Code and OpenCode today, with more agents coming. Your API key stays with you — Grass never touches it. The free tier is 10 hours with no credit card required: codeongrass.com.
Putting it together: which strategy for which situation
Throwaway container, no real data, testing a new agent behavior: --dangerously-skip-permissions. Fast, zero friction, consequences are bounded.
Long-running development task on a real VPS, you know exactly what the agent needs: Allowlist via settings.json. Takes 10 minutes to configure, gives you deterministic unattended operation.
General-purpose development on isolated infra, you trust the model's judgment: Auto Mode. Good balance for most day-to-day coding tasks.
Production-adjacent work, you want real oversight without being at your desk: Grass permission forwarding. You approve each tool call from your phone without blocking the agent when you're available.
FAQ
Is --dangerously-skip-permissions safe if I'm using a Docker container?
Mostly. If the container has no mounted volumes with real data, no access to production credentials, and gets destroyed after the task, the blast radius is contained. The remaining risk is the agent generating output you didn't want — malformed commits, broken configs — that persist if you copy results out of the container. Use it in containers that are truly ephemeral and network-isolated.
Can I combine Auto Mode with a deny list in settings.json?
Yes. Auto Mode controls the model's self-approval behavior for tools not covered by your settings. An explicit deny list in settings.json takes precedence — denied tools are blocked even if Auto Mode would have approved them. This is the right setup for most VPS workflows: Auto Mode for routine operations, explicit deny rules for anything you never want the agent to touch.
Why does my agent keep stalling on the VPS even with settings.json configured?
The most common cause is the agent requesting a tool not on your allowlist. Run the agent in a tmux session and attach to check what it's waiting on:
tmux attach -t agent-session
The prompt will show which tool triggered the approval request. Add that tool to your allowlist or deny list, then restart.
Does Claude Code's allowlist support regex, or only globs?
Only glob-style prefix matching. Bash(git*) matches any command starting with git. Full regex is not supported in settings.json as of May 2026. If you need finer-grained control — for example, allowing git commit but not git push — write separate entries for each allowed command.
How do Codex CLI's allowed_commands differ from Claude Code's allowlist?
Codex uses exact-match by default in allowed_commands and prefix-match via allowed_prefixes. Claude Code's settings.json entries use glob matching throughout. Practically, Codex's config is stricter by default — npm test in allowed_commands won't match npm test -- --watch. Claude Code's Bash(npm test*) would. Check your Codex config carefully for exact-vs-prefix semantics.