--- # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 title: "Grant GitHub Push Access to a Sandboxed Agent" sidebar-title: "GitHub Push Access" slug: "tutorials/github-push-access" description: "Launch an agent with a GitHub provider, diagnose a denied push, and grant repository-scoped write access." keywords: "Generative AI, Cybersecurity, Tutorial, GitHub, Sandbox, Policy, Claude Code" --- This tutorial shows how provider access and sandbox policy work together. You attach a GitHub provider that contributes read-only GitHub access, observe a denied push, and add a user policy that permits writes to one repository. The built-in [default policy](/how-it-works/policies/default-policy) does not grant network access. Imported provider profiles contribute the endpoints and executable paths needed by their providers. ## Prerequisites You need: - A working OpenShell installation and active gateway. - An OCI image you built with Claude Code, `git`, and the GitHub CLI installed. - An Anthropic API key. - A GitHub fine-grained personal access token with read and write access to the target repository's contents. - A scratch GitHub repository that you can push to. Use two host terminals. Terminal 1 runs the agent. Terminal 2 inspects denials and updates policy. ## Import the Provider Profiles Review the [Claude Code profile](https://github.com/NVIDIA/OpenShell/blob/main/providers/claude-code.yaml) and [GitHub profile](https://github.com/NVIDIA/OpenShell/blob/main/providers/github.yaml). Confirm that their `binaries` paths match the executables in your image, then import them directly. If a path or endpoint grant needs to change, edit a local copy and import it with `-f` instead. ```shell openshell profile import --url https://raw.githubusercontent.com/NVIDIA/OpenShell/main/providers/claude-code.yaml --global openshell profile import --url https://raw.githubusercontent.com/NVIDIA/OpenShell/main/providers/github.yaml --global ``` The gateway contains no built-in profiles. Import each profile once per gateway at the scope where users need it. ## Create the Providers Create provider instances from credentials in your host environment. Passing a credential on the same command line exports it only to that command: ```shell ANTHROPIC_API_KEY= \ openshell provider create --name my-claude --type claude-code --from-existing GITHUB_TOKEN= \ openshell provider create --name my-github --type github --from-existing ``` OpenShell stores the credentials through the configured credential driver. The agent receives opaque placeholders; the sandbox proxy resolves them only for endpoints allowed by the corresponding profile. ## Start the Agent In terminal 1, start Claude Code from your image and attach both providers: ```shell openshell sandbox create \ --name github-demo \ --from registry.example.com/your-org/claude-agent:latest \ --provider my-claude \ --provider my-github \ -- claude ``` To use an existing sandbox, attach the providers from a host terminal, then start a new agent process so it receives the provider environment variables: ```shell openshell sandbox provider attach my-claude openshell sandbox provider attach my-github openshell sandbox connect ``` ## Attempt a Push Ask the agent to create a file and push it to your scratch repository. Replace `` and `` with the repository owner and name: ```md title="Prompt" wordWrap showLineNumbers={false} Create `hello_world.py`, commit it, and push it to `https://github.com//.git`. Use the GitHub credentials already available in the environment. Do not ask me to paste a token. ``` The push fails. The GitHub profile permits clone and fetch operations but does not permit `git-receive-pack`, the Git Smart HTTP operation used for a push. The credential remains attached and endpoint-scoped; changing the token would not grant the missing network authority. ## Inspect the Denial In terminal 2, inspect recent sandbox logs: ```shell openshell logs github-demo --since 5m --source sandbox ``` You should see a denial for a request resembling this one: ```text [1775014140.412] [sandbox] [OCSF ] [ocsf] HTTP:POST [MED] DENIED POST http://github.com:443//.git/git-receive-pack [policy:_provider_my_github engine:l7] [reason:L7_REQUEST deny POST github.com:443//.git/git-receive-pack reason=POST //.git/git-receive-pack not permitted by policy] ``` `_provider_my_github` is the rule that OpenShell composes from the attached `my-github` provider. Policy events are INFO-level log records, so do not filter them out with `--level warn`. You can also run `openshell term` to inspect policy decisions in the terminal dashboard. ## Create a Repository-Scoped Policy `policy set` replaces the complete base policy, so start from the sandbox's current one. In terminal 2, print it: ```shell openshell policy get github-demo --base ``` The command prints revision details followed by the policy. Save only the policy YAML as `github-push.yaml`. Keep its filesystem, Landlock, and process settings unchanged, because OpenShell rejects removed filesystem paths and changed Landlock or process settings on a running sandbox. Add these entries under `network_policies`, creating the section if it is missing. Replace `` and ``, and adjust `binaries` to match your image: ```yaml network_policies: github_repository_push: name: github-repository-push endpoints: - host: github.com port: 443 protocol: rest enforcement: enforce rules: - allow: method: GET path: "//.git/info/refs*" - allow: method: POST path: "//.git/git-upload-pack" - allow: method: POST path: "//.git/git-receive-pack" binaries: - path: /usr/bin/git - path: /usr/local/bin/git github_repository_api: name: github-repository-api endpoints: - host: api.github.com port: 443 protocol: rest enforcement: enforce rules: - allow: method: "*" path: "/repos///**" binaries: - path: /usr/bin/gh - path: /usr/local/bin/gh ``` The first entry grants Git Smart HTTP operations only for the selected repository. The second lets `gh` use REST operations scoped to the same repository. The attached GitHub provider continues to supply credential placement and its broader read-only rules. ## Apply the Policy Apply the policy and wait for the new revision to load: ```shell openshell policy set github-demo --policy github-push.yaml --wait ``` Network policy changes hot-reload without recreating the sandbox. Provider rules are composed with this user-authored base policy. ## Retry and Verify Ask the agent to retry the push. Then confirm that the proxy allowed the scoped request: ```shell openshell logs github-demo --since 5m openshell policy get github-demo --full ``` The push succeeds for the selected repository. Requests to push to another repository remain denied. ## Clean Up Delete the sandbox when you are finished: ```shell openshell sandbox delete github-demo ``` ## Next Steps - Review [Profiles](/how-it-works/providers/profiles) for endpoint-scoped credential placement. - Review [Manage Sandbox Policies](/how-it-works/policies/manage-policies) for incremental policy updates and policy history. - Review the [Policy Schema](/how-it-works/policies/schema) for REST, GraphQL, and other protocol rules.