This tutorial walks through an iterative sandbox policy workflow. You launch a sandbox, ask Claude Code to push code to GitHub, and observe the default network policy denying the request. You then diagnose the denial from your machine and from inside the sandbox, apply a policy update, and verify that the policy update to the sandbox takes effect.
After completing this tutorial, you have:
This tutorial shows example prompts and responses from Claude Code. The exact wording you see might vary between sessions. Use the examples as a guide for the type of interaction, not as expected output.
This tutorial requires the following:
repo scope. Generate one from the GitHub personal access token settings page by selecting Generate new token (classic) and enabling the repo scope.This tutorial uses two terminals to demonstrate the iterative policy workflow:
openshell sandbox create and interact with Claude Code inside it.openshell term and applying an updated policy with openshell policy set.Each section below indicates which terminal to use.
Depending on whether you start a new sandbox or use an existing sandbox, choose the appropriate tab and follow the instructions.
In terminal 2, create a new sandbox with Claude Code. The default policy is applied automatically, which allows read-only access to GitHub.
Create a credential provider that injects your GitHub token into the sandbox automatically. The provider reads GITHUB_TOKEN from your host environment and sets it as an environment variable inside the sandbox:
openshell sandbox create keeps the sandbox running after Claude Code exits, so you can apply policy updates later without recreating the environment. Add --no-keep if you want the sandbox deleted automatically instead.
Claude Code starts inside the sandbox. It prints an authentication link. Open it in your browser, sign in to your Anthropic account, and return to the terminal. When prompted, trust the /sandbox workspace to allow Claude Code to read and write files.
In terminal 1, ask Claude Code to write a simple script and push it to your repository. Replace <org> with your GitHub organization or username and <repo> with your repository name.
Claude recognizes that it needs GitHub credentials. It asks how you want to authenticate. Provide your GitHub personal access token by pasting it into the conversation. Claude configures authentication and attempts the push.
The push fails. Claude reports an error, but the failure is not an authentication problem. The default sandbox policy permits read-only access to GitHub and blocks write operations, so the proxy denies the push before the request reaches the GitHub server.
In this section, you diagnose the denial from your machine and from inside the sandbox.
In terminal 2, launch the OpenShell terminal:
The dashboard shows sandbox status and a live stream of policy decisions. Look for entries with l7_decision=deny. Select a deny entry to see the full detail:
The log shows that the sandbox proxy intercepted an outbound PUT request to api.github.com and denied it. The github_rest_api policy allows read operations (GET) but blocks write operations (PUT, POST, DELETE) to the GitHub API. A similar denial appears for github.com if Claude attempted a git push over HTTPS.
In terminal 1, ask Claude Code to check the sandbox logs for denied requests:
Claude reads the deny entries and identifies the root cause. It explains that the failure is a sandbox network policy restriction, not a token permissions issue. For example, the following is a possible response:
The sandbox runs a proxy that enforces policies on outbound traffic.
The github_rest_api policy allows GET requests (used to read the file)
but blocks PUT/write requests to GitHub. This is a sandbox-level restriction,
not a token issue. No matter what token you provide, pushes through the API
are blocked until you update the policy.
Both perspectives confirm the same thing: the proxy is doing its job. The default policy is designed to be restrictive. To allow GitHub pushes, you need to update the network policy.
Copy the deny reason from Claude’s response. You paste it into an agent running on your machine in the next step.
In terminal 2, paste the deny reason from the previous step into your coding agent on your machine, such as Claude Code or Cursor, and ask it to recommend a policy update. The deny reason gives the agent the context it needs to generate the correct policy rules. After pasting the following prompt sample, properly provide the GitHub organization and repository names of the repository you are pushing to.
The following steps outline the expected process done by the agent:
github_git and github_api blocks that grant write access to your repository./tmp/sandbox-policy-update.yaml.Refer to the following policy example to compare with the generated policy before applying it. Confirm that the policy grants only the access you expect. In this case, git push operations and GitHub REST API access scoped to a single repository.
The following YAML shows a complete policy that extends the default policy with GitHub access for a single repository. Replace <org> with your GitHub organization or username and <repo> with your repository name.
The filesystem_policy, landlock, and process sections are static. OpenShell reads them at sandbox creation, and a hot reload cannot change them. They are included here for completeness so the file is self-contained, but only the network_policies section takes effect when you apply this to a running sandbox.
The following table summarizes the two GitHub-specific blocks:
The remaining blocks (claude_code, nvidia_inference, pypi, vscode) are identical to the default policy. The default policy’s github_ssh_over_https and github_rest_api blocks are replaced by the github_git and github_api blocks above, which grant write access to the specified repository. Sandbox behavior outside of GitHub operations is unchanged.
For details on policy block structure, refer to Policies.
After you have reviewed the generated policy, apply it to the running sandbox:
Network policies are hot-reloadable. The --wait flag blocks until the policy engine confirms the new revision loaded, and the update takes effect immediately without restarting the sandbox or reconnecting Claude Code.
The following resources cover related topics in greater depth: