> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/ai-workbench/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/ai-workbench/_mcp/server.

# Configure Claude Code Sandboxing in a Project Container

Use this guide to create a sandbox configuration repository for Claude Code and include it in a project container build.

For the full list of quickstarts, see [Quickstart Guides](/ai-workbench/quickstart/overview).

In this quickstart, you will:

- [Prerequisites](/ai-workbench/quickstart/quickstart-claude-sandbox#prerequisites) - Verify you have what you need.
- [Key Concepts](/ai-workbench/quickstart/quickstart-claude-sandbox#key-concepts) - Understand the configuration files and how they work.
- [Create a Sandbox Configuration Repository](/ai-workbench/quickstart/quickstart-claude-sandbox#create-a-sandbox-configuration-repository) - Create a configuration repository with settings, hooks and skills.
- [Include the Configuration in the Build](/ai-workbench/quickstart/quickstart-claude-sandbox#include-the-configuration-in-the-build) - Clone the repository into the container during the build.

## Prerequisites

**Before starting, ensure that:**

> 1. You have completed [Install Claude Code in a Project Container](/ai-workbench/quickstart/quickstart-claude-install).
> 2. You have a GitHub or GitLab account where you can create a public repository.

## Key Concepts

**settings.json**

Configuration file that Claude reads on session start.
Settings follow a precedence hierarchy: managed (server/MDM/file) > CLI args > `.claude/settings.local.json` (gitignored) > `.claude/settings.json` (committed) > `~/.claude/settings.json` (user level).
Arrays merge across scopes (concatenate and deduplicate). Scalars use the highest-priority scope. Deny always wins over allow at any level.

**Sandbox Block**

Block in `settings.json` that restricts what files and network endpoints Claude's processes can access.
Uses `bubblewrap` for filesystem and network isolation and `socat` for proxied network access through domain allowlists.
In container environments, bubblewrap runs in a reduced capability mode (`enableWeakerNestedSandbox`) that retains filesystem, network and process isolation but cannot mount a fresh `/proc`.

**Permissions Block**

Block in `settings.json` that gates Claude's tool calls, including Bash.
Rules are evaluated in order: deny > ask > allow, first match wins.
Bash patterns are glob-style and shell-operator-aware. Read/Edit patterns use gitignore-style globs.

Deny rules on Read and Edit only block Claude's built-in file tools, not bash subprocesses.
For example, `Read(./.env)` blocks the Read tool but does not prevent `cat .env`.
Use sandbox `denyRead` rules to enforce OS-level file access restrictions.

**Hooks Block**

Block in `settings.json` that defines commands or scripts that run on agent lifecycle events.
Claude Code supports 26 hook events. Key events for containers include `SessionStart`, `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `PermissionRequest` and `Stop`.
Hooks can block execution, modify tool input (via `updatedInput` in the output JSON) and override permission decisions.
Matchers use regex syntax, not glob. [See the full hooks reference](https://code.claude.com/docs/en/hooks-guide).

**Hook Scripts**

Standalone bash scripts referenced in the hooks block in `settings.json`.
Easier to use for more complex logic or processes than the individual commands set in `settings.json`.

**AI Workbench Skills Folder**

A sub-directory of `~/.claude/skills/` containing a skill package for AI Workbench.
Has a `SKILL.md` file and a `references/` folder with supporting documents about the project container and AI Workbench.
Claude loads skills automatically when the activation condition in the skill's frontmatter is met.

## Create a Sandbox Configuration Repository

**Step One: Create a public repository with the following structure.**

The repository will be cloned to `~/.claude/` inside the container.

```text
nvwb-claude/
├── settings.json                          # Permissions, sandbox, hooks, model
├── hooks/
│   ├── startup-claude.sh                  # SessionStart: inject project context
│   └── block-secrets-in-bash.sh           # PreToolUse: block secret exfiltration
└── skills/
    └── ai-workbench-container/
        ├── SKILL.md                       # Project container rules and constraints
        └── references/
            └── config-files.md            # AI Workbench file reference for Claude
```

**Step Two: Create a settings.json file.**

The settings file configures the model, process sandbox, permissions and hooks.
Protect it from agent modification by including it in the sandbox `denyWrite` list.

```json
{
  "model": "claude-opus-4-6",
  "sandbox": {
    "enabled": true,
    "enableWeakerNestedSandbox": true,
    "allowUnsandboxedCommands": false,
    "filesystem": {
      "denyWrite": [
        "~/.claude/settings.json",
        "~/.claude/skills/ai-workbench-container",
        "~/.claude/hooks",
        "~/.claude.json",
        "~/.claude/credentials.json",
        "~/setup.sh",
        "/project/.claude/settings.local.json",
        "/project/.claude/hooks"
      ],
      "denyRead": [
        "~/.claude.json",
        "~/.claude/credentials.json"
      ]
    },
    "autoAllowBashIfSandboxed": false
  },
  "permissions": {
    "additionalDirectories": [
      "~/",
      "/tmp/",
      "/project/"
    ],
    "ask": [
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(pip install *)",
      "Bash(pip3 install *)",
      "Bash(python -m pip install *)",
      "Bash(python3 -m pip install *)",
      "Bash(sudo *)",
      "Bash(docker *)",
      "Bash(podman *)"
    ]
  },
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "$HOME/.claude/hooks/startup-claude.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$HOME/.claude/hooks/block-secrets-in-bash.sh"
          }
        ]
      }
    ]
  }
}
```

**\`\`model\`\` sets the default model for sessions in the container.**

Users can override it per session.

**\`\`sandbox\`\` example block protects the agent's own configuration and credentials from modification.**

The `denyWrite` list covers everything that defines the agent's constraints.
The `denyRead` list blocks access to authentication tokens.
`autoAllowBashIfSandboxed` is false so bash commands still require approval even with the sandbox active.

`enableWeakerNestedSandbox` activates the container-compatible sandbox mode.
Filesystem, network and process isolation still apply; the only limitation is that `/proc` is inherited from the container rather than mounted fresh.

`allowUnsandboxedCommands` is false to prevent Claude from retrying failed sandboxed commands without the sandbox.

<Note title="Sandbox and permissions enforce at different levels.">

Deny rules in the permissions block only block Claude's built-in tools, not bash subprocesses.
For example, `Read(./.env)` blocks the Read tool but `cat .env` bypasses it.
The sandbox `denyRead` list enforces file access at the OS level regardless of how the file is accessed.
Use both layers: permissions for tool gating, sandbox for OS-level enforcement.

</Note>

**\`\`permissions\`\` example block prevents commands that would modify the container environment.**

Package installation, sudo and container runtime commands are denied.
Git staging and committing require approval.
`additionalDirectories` extends scope to paths the container uses for configuration and mounted files.

**\`\`hooks\`\` example injects project context at session start, blocks secret access and logs tool calls.**

`SessionStart` outputs the project spec, settings and GPU information into the session.
`PreToolUse` blocks commands that reference secret environment variables from `spec.yaml`.

**Step Three: Create hook scripts.**

> The SessionStart hook checks whether the container is an AI Workbench project and injects context into the session.
> It checks the project repository and creates a `CLAUDE.md` file and `.claude` subfolder if they don't exist.
> It also enters the project spec and settings, and reports GPU availability.
>
> ```bash
> #!/bin/bash
>
> # Check if Claude is in a project container
> if [ ! -f /project/.project/spec.yaml ]; then
>    echo "Not in a Workbench project container; Ignore Workbench skills"
>    exit 0
> fi
>
> # Create CLAUDE.md if it doesn't exist
> mkdir -p /project/.claude
> if [ ! -f /project/CLAUDE.md ]; then
> cat << 'EOF' > /project/CLAUDE.md
>
> # Context for this project
>
> ## This is an AI Workbench Project
>
> - This is a container
> - There are relevant skills in `~/.claude/skills/ai-workbench-container`
> - The project repository is at `/project`
>
> EOF
>
> fi
>
> # Output project context
> cat << 'EOF'
> ==============Claude Settings Information===============
> This is an AI Workbench Project container.
> The project Git repository is located at `/project`.
> The project structure can be found in `/project/.project/spec.yaml`.
> Use the Workbench skills in `~/.claude/skills/ai-workbench-container`
> EOF
>
> echo -e "This is the project spec.yaml file.\n"
> cat /project/.project/spec.yaml
>
> echo "These are the settings from ~/.claude/settings.json."
> cat ~/.claude/settings.json
>
> # Report GPU availability
> if command -v nvidia-smi &>/dev/null && nvidia-smi &> /dev/null; then
>    echo -e "\nThere are GPUs available in this container.\n"
>    nvidia-smi --query-gpu=index,name,memory.total --format=csv,noheader
> fi
> ```
>
> The PreToolUse hook blocks bash commands that reference secret environment variables defined in spec.yaml.
> It parses the project's `spec.yaml` for secret variable names and rejects any bash command that references them.
> It also blocks environment-dumping commands (`env`, `printenv`, `set`, `declare -p`) when secrets are present.
>
> ```bash
> #!/bin/bash
>
> INPUT=$(cat)
> COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
>
> SPEC="/project/.project/spec.yaml"
> if [ ! -f "$SPEC" ]; then
>   exit 0
> fi
>
> # Extract secret variable names from spec.yaml
> SECRETS=$(python3 -c "
> import yaml, sys
> try:
>     spec = yaml.safe_load(open('$SPEC'))
>     secrets = spec.get('execution', {}).get('secrets', [])
>     for s in secrets:
>         print(s['variable'])
> except Exception:
>     sys.exit(0)
> ")
>
> if [ -z "$SECRETS" ]; then
>   exit 0
> fi
>
> # Block env-dumping commands when secrets exist
> if echo "$COMMAND" | grep -qxE '\s*(env|printenv|set|declare -p|export -p)\s*'; then
>   echo "Blocked: command dumps all environment variables" >&2
>   exit 2
> fi
>
> if echo "$COMMAND" | grep -qE 'cat\s+/proc/self/environ'; then
>   echo "Blocked: reading /proc/self/environ exposes secrets" >&2
>   exit 2
> fi
>
> # Block commands that reference any secret by name
> while IFS= read -r secret; do
>   if echo "$COMMAND" | grep -qF "$secret"; then
>     echo "Blocked: command references secret variable '$secret'" >&2
>     exit 2
>   fi
> done <<< "$SECRETS"
>
> exit 0
> ```

**Step Four: Create the SKILL.md file.**

The skill tells Claude about the constraints of the project container.

```markdown
---
name: nvwb-project
description: Apply when working in a Linux environment that has a
  `/project/.project/spec.yaml` file. This skill enforces rules about
  AI Workbench project and container structure, environment configuration,
  and rebuild/restart requirements.
user-invocable: false
---

# NVIDIA AI Workbench — In-Container Project Awareness

## What NOT to Do

- Do NOT use `sudo`
- Do NOT install packages
- Do NOT edit `/project/.project/*` without consulting the user
- Do NOT run `nvwb` commands (Workbench CLI not available here)
- Do NOT print or log any secret environment variables listed in the
  `execution.secrets` section of `/project/.project/spec.yaml`

## What To Do

- Tell the user to rebuild the container after editing:
  `requirements.txt`, `apt.txt`, `postBuild.bash`, `preBuild.bash`
- Tell the user to restart the container after editing:
  `variables.env`, mount or app fields in `spec.yaml`
- Tell the user to restart the Compose application after editing:
  `compose.yaml` or `docker-compose.yml`
- For persistent storage, tell the user to add a mount
  (Claude cannot configure mounts from within the container)
```

**Step Five: Optionally, create a references/config-files.md file.**

This gives Claude detailed knowledge about each AI Workbench configuration file.
It documents location, format, edit constraints and effect of changes for files like `spec.yaml`, `apt.txt`, `requirements.txt` and build scripts.
See the [full file in the template repository](https://github.com/nv-twhitehouse/nvwb-claude/blob/main/skills/ai-workbench-container/references/config-files.md).

**Step Six: Publish the repository.**

Push the repository to GitHub or GitLab. It should be accessible from inside the project container during the build.

## Include the Configuration in the Build

**Step One: Add the clone to postBuild.bash.**

Add the following command after the Claude Code installation commands from [Install Claude Code in a Project Container](/ai-workbench/quickstart/quickstart-claude-install).

```bash
# Clone your sandbox configuration to the ~/.claude folder
git clone <url-to-your-config-repo> ~/.claude
```

**Step Two: Build the container.**

1. Select **Project Tab > Project Container > Build**

**Step Three: Verify the configuration is in place.**

1. Open a terminal in the container
2. Run `cat ~/.claude/settings.json` to verify the settings file is present
3. Run `claude` to start a session and confirm the SessionStart hook outputs project context

**Success: The sandbox configuration repository is cloned into the container at build time. Claude Code starts with your settings, hooks and skills in place.**

<Note title="Persisting runtime changes with a volume mount.">

The build clones a fresh copy of the configuration every time.
If you want runtime changes (such as conversation history) to persist across restarts, set a volume mount for `~/.claude/`.
Select **Project Tab > Project Container > Mounts > Add** with **Type > Volume Mount** and **Target Directory** set to the configuration directory.
Note that volume mount contents take precedence over build output — to reset to the build-time state, remove the volume mount and rebuild.

</Note>

## Related Topics

- [Install Claude Code in a Project Container](/ai-workbench/quickstart/quickstart-claude-install) - Install Claude Code in a project container
- [Sandboxing Agents with AI Workbench](/ai-workbench/concepts/sandboxing-concept) - How agent sandboxing works in AI Workbench
- [Claude Code Settings Reference](https://code.claude.com/docs/en/settings) - Full settings documentation
- [Claude Code Hooks Guide](https://code.claude.com/docs/en/hooks-guide) - Hooks documentation
- [Claude Code Permissions](https://code.claude.com/docs/en/permissions) - Permissions documentation