Skip to main content

Environment Sandboxing & Customization

11 min read

What You Will Learn

This chapter is all about locking down and personalizing your Claude Code setup. You’ll learn how the four tier configuration system controls settings across enterprise, personal, and project scopes, including the drop-in directory for layered enterprise policies. We’ll dig into how sandbox modes isolate Claude Code’s access to your filesystem and network. And you’ll see how to customize the terminal experience with environment variables, display modes, and a status line. By the end, you’ll be able to configure Claude Code to match your team’s security requirements and your own workflow preferences.


Configuration Scopes

Claude Code organizes its settings into four tiers, each with its own purpose and priority level. When the same setting shows up at multiple tiers, higher priority tiers win.

Managed Settings

Managed settings sit at the top of the hierarchy and can’t be overridden by anything else. Enterprise admins use these to enforce organization-wide policies, such as blocking certain commands across all developers or requiring sandbox mode.

~/.claude/managed-settings.json

Managed settings location, controlled by enterprise admins

Most individual developers won’t ever need to create this file. It’s really there for organizations that need centralized control over Claude Code’s behavior across their engineering teams. If a managed setting denies a command, no project or personal setting can override that decision.

Drop-in Policy Directory

For organizations managing multiple policy layers, Claude Code also supports a managed-settings.d/ directory alongside the main file. Each .json file in this directory is a policy fragment that gets merged into the final managed settings.

~/.claude/managed-settings.d/

Drop-in directory for layered enterprise policy fragments

Files are merged alphabetically by filename, with later files overriding earlier ones for the same key. This makes it straightforward to layer policies from different sources, for example a base security policy, a department-specific overlay, and a compliance add-on.

~/.claude/managed-settings.d/10-security-baseline.json
{
"permissions": {
"deny": [
"Bash(curl * | bash)",
"Bash(wget * | bash)"
]
},
"sandbox": {
"enabled": true
}
}

A base security policy fragment denying download-and-execute patterns

~/.claude/managed-settings.d/20-platform-team.json
{
"permissions": {
"deny": [
"Bash(docker push *)",
"Bash(kubectl apply *)"
]
}
}

A platform team overlay blocking production deployment commands

In this example, the final managed settings would include deny rules from both fragments. The 10-security-baseline.json file loads first, then 20-platform-team.json merges on top. If both files set the same key, the later file wins.

The drop-in directory is particularly useful for configuration management tools like Chef, Puppet, or Ansible that can drop policy fragments without coordinating writes to a single file. For a deeper look at enterprise governance patterns with managed settings, see Security Deep Dive & Governance.

User Settings

User settings are your personal defaults that apply across every project on your machine. This is where you’d set things like your default model, effort level, and personal permission rules.

~/.claude/settings.json

User settings location, personal defaults for all projects

Project Settings

Project settings get shared with your team through version control. This is the right place for permission rules everyone on the team should follow, model preferences for the project, and any project-specific configuration.

.claude/settings.json

Project settings location, committed to git, shared with team

Local Settings

Local settings are your personal overrides for a specific project. They don’t get committed to git and only exist on your machine. Use these for personal preferences that differ from the team defaults. For example, you might use a different model or enable auto-allow sandbox mode for a project you trust.

.claude/settings.local.json

Local settings location, personal project overrides, not committed to git

Precedence Order

When the same setting exists at multiple tiers, the precedence order is: managed > local > project > user. Notice that local overrides project, which lets individual developers customize their experience without touching team settings.

In practice, that means:

  • A managed deny rule can’t be overridden by any other tier
  • A local setting takes precedence over the project setting for the same key
  • A project setting overrides your user level default
  • User settings act as the fallback when nothing else is specified

Settings File Format

All four tiers use the same JSON format. Here’s a comprehensive example showing the most commonly used fields.

.claude/settings.json
{
"model": "sonnet",
"permissions": {
"mode": "default",
"allow": [
"Bash(npm run *)",
"Bash(npm test *)",
"Bash(git add *)",
"Bash(git commit *)",
"Read(*)"
],
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(rm -rf /)*"
]
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": false,
"filesystem": {
"allowWrite": [
"./src/**",
"./tests/**",
"./docs/**"
]
}
},
"preferences": {
"effort": "medium"
}
}

Comprehensive project settings showing permissions, sandbox, and preferences

The permissions section controls which actions Claude can take without asking (covered in detail in Models, Cost Economics & Permissions). The sandbox section handles filesystem and network isolation (we’ll cover that below). And the preferences section sets behavioral defaults like effort level.

Environment Variables

A handful of environment variables control Claude Code’s behavior. Set them in your shell profile so they persist across sessions.

~/.zshrc
Terminal window
# Model selection
export ANTHROPIC_MODEL=sonnet
# Effort level (low, medium, high)
export CLAUDE_CODE_EFFORT=medium
# API key (for direct API usage, not needed for Claude Pro/Max subscriptions)
export ANTHROPIC_API_KEY=sk-ant-...
# Custom output directory for generated files
export CLAUDE_CODE_OUTPUT_DIR=./output
# Disable automatic updates
export DISABLE_AUTOUPDATER=1
# Default haiku model for background tasks
export ANTHROPIC_DEFAULT_HAIKU_MODEL=haiku

Key environment variables for Claude Code configuration

These variables take effect for the current shell session and any Claude Code sessions launched from it. For permanent changes, add them to your shell profile (~/.zshrc, ~/.bashrc, or equivalent).

Display and Input Variables

Several environment variables control how Claude Code renders its terminal interface and handles input.

~/.zshrc
Terminal window
# Enable flicker-free alt-screen rendering
export CLAUDE_CODE_NO_FLICKER=1
# Disable mouse capture (keeps flicker-free rendering but without mouse interaction)
export CLAUDE_CODE_DISABLE_MOUSE=1
# Adjust scroll speed (1-20, default varies by terminal)
export CLAUDE_CODE_SCROLL_SPEED=5

Display customization variables for the terminal interface

CLAUDE_CODE_NO_FLICKER=1 switches Claude Code to an alt-screen renderer, similar to how vim or less take over the terminal. The prompt stays pinned to the bottom, only visible content gets rendered, and memory usage stays flat regardless of conversation length. Mouse support is included by default: click to expand tool results, click to position the cursor, drag to select text with auto-copy, and click URLs to open them.

If you want the flicker-free rendering but your workflow relies on terminal mouse features that conflict with Claude Code’s mouse capture, set CLAUDE_CODE_DISABLE_MOUSE=1 alongside the no-flicker variable. You get the stable rendering without any mouse interception.

The flicker-free renderer works well in tmux (with set -g mouse on in your tmux config) but is incompatible with iTerm2’s tmux -CC integration mode.

Transcript Mode

Whether or not you use flicker-free rendering, pressing Ctrl+O opens transcript mode, a searchable view of your entire conversation history. Think of it like less for your Claude Code session.

In transcript mode:

  • Press / to search, then n and N to step through matches
  • Use j/k or arrow keys to scroll, g/G to jump to top or bottom
  • Press [ to write the full conversation to your terminal’s native scrollback for terminal-level search
  • Press v to open the transcript in your $EDITOR
  • Press Esc to return to the normal prompt

Transcript mode is especially useful for long sessions where you need to find a specific command, error message, or explanation that scrolled off screen.

Platform-Specific Variables

Terminal window
# Enable PowerShell as the shell tool on Windows
export CLAUDE_CODE_USE_POWERSHELL_TOOL=1

Windows-specific: use PowerShell instead of the default Bash tool

On Windows, setting CLAUDE_CODE_USE_POWERSHELL_TOOL=1 tells Claude Code to use PowerShell for shell commands instead of the default Bash tool. This gives you native cmdlet support and proper Windows path handling.

CLI Flags

Claude Code accepts several flags at startup that change how the session runs. One particularly useful flag for scripting and automation is --bare.

Terminal window
claude --bare "Summarize the changes in the last commit"

Bare mode: minimal startup, skips CLAUDE.md loading, great for quick scripting

The --bare flag starts Claude Code with minimal initialization. It skips loading CLAUDE.md files, project context, and other startup overhead. This makes it ideal for quick one-off questions, shell scripts that call Claude Code for a single task, and CI pipelines where you want fast responses without project context loading.


Why Sandboxing Matters

Claude Code runs real commands on your machine. It reads files, executes shell commands, and writes code. That power is exactly what makes it so effective, but it also means unexpected behavior could have real consequences.

Sandboxing constrains what Claude Code can access, even when something goes wrong. Two main risk scenarios make this important:

Accidental damage. Claude might misinterpret a request and fire off a destructive command like rm -rf on a directory outside your project. Without sandboxing, that would succeed and your data would be gone. With sandboxing, filesystem access stays restricted to authorized paths.

Prompt injection. When you’re working with untrusted codebases, malicious content hidden in files or commit messages could try to trick Claude into running harmful commands. Sandboxing limits the blast radius; even if Claude gets manipulated into running something, it can’t reach files or network resources outside the sandbox boundary.

Here’s the key detail: sandboxing works at the operating system level, not the application level. Claude Code can’t bypass it even with a bypassPermissions mode because the OS kernel enforces the restrictions.

Sandbox Modes

Claude Code offers two sandbox modes, each striking a different balance between security and convenience.

Regular Sandbox

With regular sandbox mode, Claude asks for permission before doing anything that might reach outside the sandbox boundary. This is the default behavior when sandboxing is enabled.

.claude/settings.json
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": false
}
}

Regular sandbox: Claude asks for permission on boundary-crossing operations

This is the safest option. You maintain full visibility into every command Claude runs while still benefiting from OS-level isolation that prevents catastrophic errors.

Auto-Allow with Sandbox

Auto-allow sandbox mode automatically approves any operation that stays within the sandbox boundary. Claude only asks for permission when something would cross that boundary.

.claude/settings.json
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true
}
}

Auto-allow sandbox: operations within the boundary proceed without prompting

The autoAllowBashIfSandboxed flag is the key setting here. When it’s set to true, Claude can run any bash command that stays within the sandbox’s filesystem and network constraints without asking. You get a fast, uninterrupted workflow while keeping hard security boundaries in place.

This mode is a great fit when you trust the codebase you’re working on and want minimal interruptions, but still want OS-level guardrails preventing access outside the project.

Filesystem Isolation

Sandboxing enforces filesystem access restrictions at the operating system level, using platform-specific mechanisms under the hood.

macOS uses Seatbelt (sandbox-exec) to restrict filesystem access at the kernel level. Seatbelt profiles define which paths are readable and writable. It’s the same sandboxing technology that macOS uses for App Store applications.

Linux uses bubblewrap (bwrap) for namespace based isolation. Bubblewrap creates a restricted filesystem view where only explicitly allowed paths are visible. It relies on Linux kernel namespaces, the same technology behind container runtimes like Docker.

Neither platform depends on Claude Code to self enforce filesystem restrictions. The OS kernel prevents unauthorized access no matter what commands Claude tries to run.

Configuring Writable Paths

The sandbox.filesystem.allowWrite setting specifies which paths Claude Code can write to. Paths follow a simple prefix notation:

PrefixMeaningExample
//Absolute path from root//tmp/claude-output
~Home directory~/Documents/exports
./Relative to project root./src/**
.claude/settings.json
{
"sandbox": {
"enabled": true,
"filesystem": {
"allowWrite": [
"./src/**",
"./tests/**",
"./docs/**",
"//tmp/claude-*",
"~/.claude/projects/**"
]
}
}
}
}

Filesystem sandbox with prefix notation for writable paths

Glob patterns work here too. ./src/** allows writing to any file under the src directory tree, while //tmp/claude-* allows writing to temporary files with a claude- prefix. Any path not listed in allowWrite is read only by default when sandboxing is enabled.

Network Isolation

The sandbox also controls network access through a proxy-based architecture. All outbound network traffic gets routed through a local proxy that enforces domain restrictions.

The allowedDomains setting controls which external domains Claude Code can reach. This helps prevent data exfiltration and limits the scope of any network-related prompt injection attacks.

.claude/settings.json
{
"sandbox": {
"enabled": true,
"network": {
"allowedDomains": [
"registry.npmjs.org",
"pypi.org",
"api.github.com",
"raw.githubusercontent.com"
]
}
}
}

Network sandbox allowing only package registries and GitHub API

Out of the box, the sandbox allows access to common package registries (npm, PyPI, crates.io) and other standard development domains. You can tighten this further for high security environments or loosen it for projects that need specific API access.

Keep the allowed domains list as small as practical. Every domain you add is a potential channel for data to leave your machine. If Claude needs to access an API that isn’t in the allowed list, you’ll be prompted to approve it.


Status Line Customization

During active sessions, Claude Code displays a status line at the bottom of your terminal. You can customize it with a shell script that receives session information and outputs whatever text you want.

Configuring the Status Line

Just point the statusLine setting to a shell script that generates your custom status text.

.claude/settings.json
{
"statusLine": "~/.claude/status.sh"
}

Point to a custom status line script

Writing a Status Line Script

Your status line script receives session JSON on stdin and should output a single line of text to stdout. The JSON includes information about the current session like the model, cost, and token usage.

~/.claude/status.sh
#!/bin/bash
# Read session JSON from stdin
SESSION=$(cat)
# Extract fields using jq
MODEL=$(echo "$SESSION" | jq -r '.model // "unknown"')
COST=$(echo "$SESSION" | jq -r '.cost // "0.00"')
TOKENS=$(echo "$SESSION" | jq -r '.tokensUsed // 0')
# Format output
echo "Model: $MODEL | Cost: $$COST | Tokens: $TOKENS"

Example status line script showing model, cost, and token count

Don’t forget to mark the script as executable:

Terminal window
chmod +x ~/.claude/status.sh

Make the status line script executable

The status line refreshes periodically during the session, so keep your script fast. It should execute in milliseconds, not seconds. Avoid network calls or expensive computations in there.

Practical Status Line Examples

Here’s a cost-focused status line for budget-conscious teams:

~/.claude/cost-status.sh
#!/bin/bash
SESSION=$(cat)
COST=$(echo "$SESSION" | jq -r '.cost // "0.00"')
MODEL=$(echo "$SESSION" | jq -r '.model // "unknown"')
echo "[$MODEL] $$COST spent"

Minimal status line showing model and running cost

Best Practices

  • Use project settings for team conventions. Commit .claude/settings.json to version control so everyone shares the same permission rules, model defaults, and sandbox configuration. That way the whole team gets consistent behavior.

  • Use local settings for personal overrides. Keep .claude/settings.local.json out of version control (add it to .gitignore). It’s the right place for personal preferences like a different default model or auto-allow sandbox mode for projects you trust.

  • Enable sandbox for untrusted codebases. Whenever you’re working with code you didn’t write, such as open source contributions, inherited projects, or third party code reviews, turn on sandboxing to limit the blast radius of any malicious content lurking in the codebase.

  • Start with regular sandbox before auto-allow. Try regular sandbox mode first to get a feel for what commands Claude runs during your typical workflow. Once you’re confident in the patterns, flip on autoAllowBashIfSandboxed for a faster experience.

  • Keep allowedDomains minimal. Only add domains your project genuinely needs. Every additional domain is a potential data exfiltration channel, so start with package registries and expand as needed.

  • Use --bare for scripting. When calling Claude Code from shell scripts or CI pipelines, the --bare flag skips project context loading and gives you faster, more predictable responses.

  • Use the status line to track costs. A custom status line showing the current session cost keeps spending visible without you having to run /cost manually. This is especially handy for teams with cost targets.

  • Document your settings choices. Add comments to your project’s CLAUDE.md explaining why specific permission rules exist. Future team members (and future you) will thank you for documenting the reasoning behind deny rules.

  • Layer enterprise policies with managed-settings.d/. Instead of maintaining one monolithic managed-settings.json, break policies into focused fragments. This simplifies auditing, enables per-team overlays, and works naturally with configuration management tools.

Further Reading

  • Settings: Official docs on configuration scopes, file locations, and available settings
  • Sandboxing: Official docs on sandbox modes, filesystem isolation, and network restrictions
  • Status Line: Official docs on custom status line scripts and session JSON format
  • Remote Control & Headless Automation: Running Claude Code remotely and automating with scheduled tasks (next chapter)