Files
OpenShell/pr-preview/pr-240/tutorials/github-sandbox.json
T
2026-03-11 22:30:42 +00:00

1 line
13 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"id":"tutorials/github-sandbox","title":"Set Up a Sandbox of Claude Code with a Custom GitHub Policy","url":"tutorials/github-sandbox.html","last_modified":"2026-03-11T22:30:38.559955+00:00","book":{"title":"NVIDIA OpenShell Developer Guide","version":"0.1.0"},"product":{"name":"OpenShell","version":"0.1.0"},"description":"Learn the iterative policy workflow by launching a sandbox, diagnosing a GitHub access denial, and applying a custom policy to fix it.","tags":["Tutorial","GitHub","Sandbox","Policy","Claude Code"],"topics":["Generative AI","Cybersecurity"],"content_type":"tutorial","learning_level":"technical_intermediate","audience":["engineer"],"content":"Set Up a Sandbox of Claude Code with a Custom GitHub Policy 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\nYou then diagnose the denial from your machine and from inside the sandbox, apply a custom policy, and verify that the policy update takes effect. After completing this tutorial, you will have: A running sandbox with Claude Code that can push to a GitHub repository. A custom network policy that grants GitHub access for a specific repository. Experience with the policy iteration workflow: fail, diagnose, update, verify. 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. Prerequisites This tutorial requires the following: A working OpenShell installation. Complete the Quickstart before proceeding. A GitHub personal access token (PAT) with repo scope. Generate one from the GitHub personal access token settings page by selecting Generate new token (classic) and enabling the repo scope. To avoid pasting the token manually during the tutorial, you can configure a credential provider to inject it automatically at sandbox startup: $ openshell provider create --name my-github --type github --from-existing\nopenshell sandbox create --provider my-github --keep -- claude\nThe provider reads GITHUB_TOKEN from your host environment and exposes it as an environment variable inside the sandbox. Claude Code can then use it directly. An Anthropic account with access to Claude Code. OpenShell provides the sandbox runtime, not the agent. You must authenticate with your own account. A GitHub repository you own to use as the push target. A scratch repository is sufficient. You can create one with a README if needed. This tutorial uses two terminals to demonstrate the iterative policy workflow: Terminal 1 : The sandbox terminal. You create the sandbox here with openshell sandbox create and interact with Claude Code inside it. Terminal 2 : A terminal outside the sandbox on your machine. You use this for viewing the sandbox logs with openshell term and applying an updated policy with openshell policy set . Each section below indicates which terminal to use. Launch the Sandbox In terminal 1, create a sandbox and start Claude Code. No custom policy is needed. The default policy is applied automatically: $ openshell sandbox create --keep -- claude\nThe --keep flag keeps the sandbox running after Claude Code exits, so you can apply policy updates later without recreating the environment. 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. Push Code to GitHub 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. Write a hello_world.py script and push it to https://github.com/<org>/<repo\nClaude 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 should fail. 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. Diagnose the Denial In this section, you diagnose the denial from your machine and from inside the sandbox. View the Logs from Your Machine In terminal 2, launch the OpenShell terminal: $ openshell term\nThe 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: l7_action: PUT\nl7_target: /repos/<org>/<repo>/contents/hello_world.py\nl7_decision: deny\ndst_host: api.github.com\ndst_port: 443\nl7_protocol: rest\npolicy: github_rest_api\nl7_deny_reason: PUT /repos/<org>/<repo>/contents/hello_world.py not permitted by policy\nThe 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. Ask Claude to Check the Sandbox Logs In terminal 1, ask Claude Code to check the sandbox logs for denied requests: Check the sandbox logs for any denied network requests. What is blocking the push\nClaude 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: The sandbox runs a proxy that enforces policies on outbound traffic. The\ngithub_rest_api policy allows GET requests (used to read the file) but blocks\nPUT/write requests to GitHub. This is a sandbox-level restriction, not a token\nissue. No matter what token you provide, pushes through the API will be blocked\nuntil the policy is updated. 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. Update the Policy from Your Machine In terminal 2, create a policy file that extends the default policy with GitHub write access for your repository. You can ask a coding agent on your machine, such as Claude Code or Cursor, to generate it from the deny reasons\nThe updated policy should have github_git and github_api blocks that grant write access to your repository. The following steps outline the expected process done by the agent: Reads the default policy. Adds github_git and github_api blocks that grant write access to your repository\nRefer to the following policy example to compare with the policy the agent generated. Full reference policy 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. They are read once at sandbox creation and cannot be changed by a hot-reload. 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. version: 1\n── Static (locked at sandbox creation)\nfilesystem_policy\ninclude_workdir: true\nread_only\n/usr\n/lib\n/proc\n/dev/urandom\n/app\n/etc\n/var/log\nread_write\n/sandbox\n/tmp\n/dev/null\nlandlock\ncompatibility: best_effort\nprocess\nrun_as_user: sandbox\nrun_as_group: sandbox\n── Dynamic (hot-reloadable)\nnetwork_policies\nClaude Code ↔ Anthropic API\nclaude_code\nname: claude-code\nendpoints\n{ host: api.anthropic.com, port: 443, protocol: rest, enforcement: enforce, access: full, tls: terminate\n{ host: statsig.anthropic.com, port: 443\n{ host: sentry.io, port: 443\n{ host: raw.githubusercontent.com, port: 443\n{ host: platform.claude.com, port: 443\nbinaries\n{ path: /usr/local/bin/claude\n{ path: /usr/bin/node\nNVIDIA inference endpoint\nnvidia_inference\nname: nvidia-inference\nendpoints\n{ host: integrate.api.nvidia.com, port: 443\nbinaries\n{ path: /usr/bin/curl\n{ path: /bin/bash\n{ path: /usr/local/bin/opencode\n── GitHub: git operations (clone, fetch, push)\ngithub_git\nname: github-git\nendpoints\nhost: github.com\nport: 443\nprotocol: rest\ntls: terminate\nenforcement: enforce\nrules\nallow\nmethod: GET\npath: \"/<org>/<repo>.git/info/refs\nallow\nmethod: POST\npath: \"/<org>/<repo>.git/git-upload-pack\nallow\nmethod: POST\npath: \"/<org>/<repo>.git/git-receive-pack\nbinaries\n{ path: /usr/bin/git\n── GitHub: REST API\ngithub_api\nname: github-api\nendpoints\nhost: api.github.com\nport: 443\nprotocol: rest\ntls: terminate\nenforcement: enforce\nrules\nGraphQL API (used by gh CLI\nallow\nmethod: POST\npath: \"/graphql\nFull read-write access to the repository\nallow\nmethod:\npath: \"/repos/<org>/<repo\nbinaries\n{ path: /usr/local/bin/claude\n{ path: /usr/local/bin/opencode\n{ path: /usr/bin/gh\n{ path: /usr/bin/curl\n── Package managers\npypi\nname: pypi\nendpoints\n{ host: pypi.org, port: 443\n{ host: files.pythonhosted.org, port: 443\n{ host: github.com, port: 443\n{ host: objects.githubusercontent.com, port: 443\n{ host: api.github.com, port: 443\n{ host: downloads.python.org, port: 443\nbinaries\n{ path: /sandbox/.venv/bin/python\n{ path: /sandbox/.venv/bin/python3\n{ path: /sandbox/.venv/bin/pip\n{ path: /app/.venv/bin/python\n{ path: /app/.venv/bin/python3\n{ path: /app/.venv/bin/pip\n{ path: /usr/local/bin/uv\n{ path: \"/sandbox/.uv/python/**\"\n── VS Code Remote\nvscode\nname: vscode\nendpoints\n{ host: update.code.visualstudio.com, port: 443\n{ host: \"*.vo.msecnd.net\", port: 443\n{ host: vscode.download.prss.microsoft.com, port: 443\n{ host: marketplace.visualstudio.com, port: 443\n{ host: \"*.gallerycdn.vsassets.io\", port: 443\nbinaries\n{ path: /usr/bin/curl\n{ path: /usr/bin/wget\n{ path: \"/sandbox/.vscode-server/**\"\n{ path: \"/sandbox/.vscode-remote-containers/**\"\nThe following table summarizes the two GitHub-specific blocks: Block Endpoint Behavior github_git github.com:443 Git Smart HTTP protocol with TLS termination. Permits info/refs (clone/fetch), git-upload-pack (fetch data), and git-receive-pack (push) for the specified repository. Denies all operations on unlisted repositories. github_api api.github.com:443 REST API with TLS termination. Permits all HTTP methods for the specified repository and GraphQL queries. Denies API access to unlisted repositories. 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 Network Access Rules . Saves the policy to a file, such as /tmp/sandbox-policy-update.yaml . Applies the policy to the running sandbox by running the following command: $ openshell policy set <sandbox-name> --policy /tmp/sandbox-policy-update.yaml --wait\nNetwork 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. Retry the Push In terminal 1, ask Claude Code to retry the push: The sandbox policy has been updated. Try pushing to the repository again\nThe push completes successfully. The openshell term dashboard now shows l7_decision=allow entries for api.github.com and github.com where it previously showed denials. Clean Up When you are finished, delete the sandbox to free cluster resources: $ openshell sandbox delete <sandbox-name\nNext Steps The following resources cover related topics in greater depth: To add per-repository access levels (read-write vs read-only) or restrict to specific API methods, refer to the Policy Schema Reference . To learn the full policy iteration workflow (pull, edit, push, verify), refer to /sandboxes/policies . To inject credentials automatically instead of pasting tokens, refer to /sandboxes/providers","format":"text","summary":"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 custom policy, and verify...","headings":[{"text":"Set Up a Sandbox of Claude Code with a Custom GitHub Policy","level":1},{"text":"Prerequisites","level":2},{"text":"Launch the Sandbox","level":2},{"text":"Push Code to GitHub","level":2},{"text":"Diagnose the Denial","level":2},{"text":"View the Logs from Your Machine","level":3},{"text":"Ask Claude to Check the Sandbox Logs","level":3},{"text":"Update the Policy from Your Machine","level":2},{"text":"Retry the Push","level":2},{"text":"Clean Up","level":2},{"text":"Next Steps","level":2}]}