Playbook
Production-ready configurations for every team size, deployment target, and security posture. Copy, adapt, ship.
Permission Architectures by Team Size
Solo Developer — Maximum Speed
Optimize for velocity. Allow common tools, deny sensitive file access.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"defaultMode": "acceptEdits",
"allow": [
"Bash(npm *)",
"Bash(git *)",
"Bash(node *)",
"Bash(npx *)",
"Bash(pnpm *)",
"Edit",
"Write"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}Why acceptEdits: Auto-accepts file writes in the working directory. You're watching the terminal anyway. The deny rules protect secrets even if you step away.
Small Team (2-10) — Balanced
Committed to .claude/settings.json. Every collaborator gets the same guardrails.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"defaultMode": "default",
"allow": [
"Bash(npm run *)",
"Bash(npm test *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git status *)",
"Bash(* --version)",
"Bash(* --help *)"
],
"deny": [
"Bash(git push *)",
"Bash(git push)",
"Bash(rm -rf *)",
"Bash(curl *)",
"Bash(wget *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.*)"
]
}
}Design rationale: Read-only git operations are allowed. Network commands and destructive operations require human approval. Every developer can add personal overrides in .claude/settings.local.json without affecting the team.
Enterprise — Locked Down
Deployed via managed settings (/etc/claude-code/managed-settings.json). Cannot be overridden by anyone.
{
"permissions": {
"disableBypassPermissionsMode": "disable",
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(ssh *)",
"Bash(scp *)",
"Bash(git push --force *)",
"Bash(git reset --hard *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)"
]
},
"allowManagedPermissionRulesOnly": true,
"disableAutoMode": "disable",
"availableModels": ["sonnet", "haiku"],
"model": "sonnet",
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"network": {
"allowManagedDomainsOnly": true,
"allowedDomains": ["github.com", "*.npmjs.org", "registry.yarnpkg.com"]
},
"filesystem": {
"allowManagedReadPathsOnly": true,
"denyRead": ["~/.ssh", "~/.aws", "~/.gnupg"]
}
}
}Key decisions: allowManagedPermissionRulesOnly prevents users from adding their own allow rules. disableBypassPermissionsMode blocks the most permissive mode. The sandbox provides OS-level enforcement that tool-level deny rules cannot.
Model Selection Strategies
Cost Optimization
Pin to cheaper models. Restrict the picker so users cannot accidentally switch to Opus.
{
"model": "sonnet",
"availableModels": ["sonnet", "haiku"],
"effortLevel": "low",
"env": {
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4-5"
}
}Speed Optimization
Sonnet with medium effort and tight output limits for fast iteration cycles.
{
"model": "sonnet",
"effortLevel": "medium",
"env": {
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "4096",
"BASH_DEFAULT_TIMEOUT_MS": "60000"
}
}Quality Optimization
Opus with 1M context and high effort for complex architectural work.
{
"model": "opus[1m]",
"effortLevel": "high",
"env": {
"MAX_THINKING_TOKENS": "32768"
}
}Hybrid — Best of Both Worlds
Opus reasons about the approach, Sonnet executes it. Best cost/quality ratio for complex tasks.
{
"model": "opusplan"
}Model Decision Matrix
| Scenario | Model | Effort | Cost |
|---|---|---|---|
| Bug triage, code review | sonnet | medium | Low |
| Feature implementation | sonnet | medium | Low |
| Architecture design | opus | high | High |
| Multi-file refactor | opusplan | high | Medium |
| CI/CD automation | sonnet | low | Minimal |
| Large codebase exploration | sonnet[1m] | medium | Low |
| Subagent tasks | haiku | low | Minimal |
Admin Model Restriction
Lock users to specific models and pin alias targets to prevent version drift:
{
"availableModels": ["sonnet", "haiku"],
"model": "sonnet",
"env": {
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-5"
}
}This pins the sonnet alias to a specific version, limits the model picker, and prevents automatic upgrades.
Profile Configurations
Work Profile
~/.claude-work/settings.json — routed through a corporate LLM gateway.
{
"model": "sonnet",
"permissions": {
"defaultMode": "acceptEdits",
"allow": [
"Bash(npm *)",
"Bash(git *)",
"Bash(docker *)",
"Bash(kubectl *)"
],
"deny": [
"Bash(git push --force *)",
"Read(./.env.production)"
]
},
"env": {
"ANTHROPIC_BASE_URL": "https://llm-gateway.company.com/v1"
},
"language": "english"
}Personal Profile
~/.claude-personal/settings.json — maximum capability for side projects.
{
"model": "opus",
"permissions": {
"defaultMode": "bypassPermissions"
},
"effortLevel": "high"
}Switching Profiles
alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'
alias claude-personal='CLAUDE_CONFIG_DIR=~/.claude-personal claude'Add the active profile to your shell prompt to avoid confusion:
export PS1="[claude:${CLAUDE_PROFILE:-default}] $PS1"Security-Focused Configurations
Block Network Operations
{
"permissions": {
"deny": [
"Bash(curl *)",
"Bash(wget *)",
"Bash(nc *)",
"Bash(ncat *)",
"Bash(ssh *)",
"Bash(scp *)",
"Bash(rsync *)",
"WebFetch"
]
}
}Protect Sensitive Files
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.*)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.gnupg/**)",
"Edit(./.github/workflows/**)"
]
}
}Warning: Read/Edit deny rules only block Claude's built-in tools, NOT Bash commands. Read(./.env) blocks the Read tool but does NOT block cat .env in Bash. For true enforcement, enable the sandbox.
Defense-in-Depth with Sandbox
Layer tool-level deny rules with OS-level sandbox enforcement:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)"
]
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"filesystem": {
"denyRead": ["~/.aws/credentials", "~/.ssh"],
"allowWrite": ["/tmp/build", "."],
"denyWrite": ["/etc", "/usr/local/bin"]
},
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"],
"allowLocalBinding": true
}
}
}The sandbox blocks cat .env at the OS level. The deny rules block the Read tool at the application level. Both layers must be bypassed for a breach.
CI/CD Configuration
Headless Execution
#!/bin/bash
export ANTHROPIC_API_KEY="${{ secrets.ANTHROPIC_API_KEY }}"
export DISABLE_TELEMETRY=1
export DISABLE_AUTOUPDATER=1
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
export CLAUDE_CODE_SKIP_PROMPT_HISTORY=1
npx @anthropic-ai/claude-code -p "Review the PR and suggest improvements" \
--model sonnet \
--permission-mode bypassPermissions \
--output-format json \
--no-session-persistence \
--bare \
--allowedTools "Read,Grep,Glob,Bash(git diff *),Bash(npm test *)"A complete GitHub Actions workflow:
# .github/workflows/claude-review.yml
name: Claude Code PR Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Claude Code Review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_MODEL: sonnet
DISABLE_TELEMETRY: "1"
DISABLE_AUTOUPDATER: "1"
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
CLAUDE_CODE_SKIP_PROMPT_HISTORY: "1"
run: |
REVIEW=$(npx @anthropic-ai/claude-code -p \
"Review the PR diff. Focus on bugs, security, and performance. Output markdown." \
--model sonnet \
--permission-mode bypassPermissions \
--output-format text \
--no-session-persistence \
--bare \
--allowedTools "Read,Grep,Glob,Bash(git diff *),Bash(git log *)")
gh pr comment ${{ github.event.pull_request.number }} --body "$REVIEW"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}Key CI Flags
| Flag | Purpose |
|---|---|
-p | Non-interactive/headless mode |
--bare | Skip auto-discovery of hooks, skills, plugins, MCP, CLAUDE.md |
--no-session-persistence | Don't write transcripts |
--output-format json | Structured output for pipeline parsing |
--allowedTools | Explicit tool allowlist — nothing else runs |
--permission-mode bypassPermissions | Skip prompts (safe in ephemeral containers) |
Why bypassPermissions is safe in CI: The container is ephemeral. There are no secrets on disk beyond what you inject. The --allowedTools flag restricts the tool set. The container dies after the run.
Auto Mode Configuration
Configure what the background classifier trusts:
{
"autoMode": {
"environment": [
"Organization: Acme Corp. Primary use: software development",
"Source control: github.com/acme-corp",
"Trusted cloud buckets: s3://acme-build-artifacts",
"Trusted internal domains: *.internal.acme.com"
]
}
}The classifier reads autoMode from user settings, .claude/settings.local.json, and managed settings. It does NOT read from shared project settings (.claude/settings.json) — this prevents repo injection of allow rules.
Critical: Setting autoMode.soft_deny or autoMode.allow replaces the ENTIRE default list. Always run claude auto-mode defaults first, copy the full lists, then edit.
# Step 1: Export current defaults
claude auto-mode defaults > auto-mode-defaults.json
# Step 2: Edit the file — ADD your entries to existing lists
# Step 3: Apply in ~/.claude/settings.json or .claude/settings.local.json{
"autoMode": {
"environment": [
"Organization: Acme Corp. Primary use: software development",
"Source control: github.com/acme-corp",
"Trusted cloud buckets: s3://acme-build-artifacts"
],
"allow": [
"Bash(npm run *)",
"Bash(git diff *)",
"Bash(git status *)",
"Read"
],
"soft_deny": [
"Bash(git push --force *)",
"Bash(curl * | bash)",
"Bash(rm -rf /)",
"Bash(*production*)"
]
}
}