On Runtime, the token never enters the sandbox. A secret is sealed when you store it. The host's proxy adds it to HTTPS requests bound for the hosts you name. The sandbox holds a placeholder that is worthless on its own. So an agent that prints its environment, or a prompt injection that tries to send the token elsewhere, has nothing to leak. git is in every sandbox. Ubuntu 24.04's package is git 2.43.0, checked 25 October 2026.
Clone over HTTPS with a header secret
GitHub accepts a token over HTTPS as HTTP Basic credentials, with the user name
x-access-token. This is the form that GitHub's own actions/checkout sends.
Store that credential once, base64-encoded, as a secret whose header is
Authorization:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const token = process.env.GITHUB_TOKEN ?? ""; // a fine-grained token for the repositories the agent needsawait runtime.secrets.set("GIT_GITHUB_AUTH", { value: Buffer.from(`x-access-token:${token}`).toString("base64"), hosts: ["github.com"], header: "Authorization", format: "Basic {value}",});await using sbx = await runtime.sandboxes.create();await sbx.exec("git clone --depth 1 https://github.com/acme/private-app.git app", { check: true, timeoutMs: 300_000,});Pythonimport base64import osfrom withruntime import Runtimeruntime = Runtime()token = os.environ["GITHUB_TOKEN"]runtime.secrets.set( "GIT_GITHUB_AUTH", value=base64.b64encode(f"x-access-token:{token}".encode()).decode(), hosts=["github.com"], header="Authorization", format="Basic {value}",)with runtime.sandboxes.create() as sbx: sbx.exec("git clone --depth 1 https://github.com/acme/private-app.git app", check=True, timeout_ms=300_000)The URL carries no credentials. With header set, the proxy sets that header on
every HTTPS request to github.com. It replaces any header of that name that
the sandbox sent. So git needs no credential helper and never prompts. The
secret applies to every sandbox in the account from then on. Replacing it
updates running sandboxes within seconds.
This works without credit. A sandbox without credit reaches ports 80 and 443.
Commit and push the agent's work
Push goes to the same host, so the same secret covers it. Set an identity for the commit, work on a branch, and push:
TypeScriptimport { Sandbox } from "withruntime";const sbx = await Sandbox.connect(process.env.SANDBOX_ID ?? "");const git = (args: string) => sbx.exec(`git -C /workspace/app ${args}`, { check: true, timeoutMs: 120_000 });await git("config user.name 'Build Agent'");await git("config user.email 'agent@example.com'");await git("switch -c agent/fix-login");await git("add -A");await git("commit -m 'Fix the login redirect'");await git("push -u origin agent/fix-login");Scope the token to what the agent should do. A fine-grained GitHub token can be
limited to chosen repositories, with only the "Contents" read and write
permission. A branch protection rule on main then keeps the agent to branches
and pull requests. To open the pull request, see
the GitHub CLI in a sandbox.
Which way to authenticate
| Method | Without credit | Paid | Where the credential lives | Notes |
|---|---|---|---|---|
| HTTPS with a header secret | Yes | Yes | Sealed on Runtime's servers | The method above |
| HTTPS, token in the URL | Yes | Yes | In the sandbox, in .git/config |
Avoid: any code in the sandbox can read it |
| SSH with a deploy key | No | Yes | In the sandbox, in ~/.ssh |
Port 22; one repository per key |
| Public repository | Yes | Yes | None | git clone https://... as is |
GitLab, Bitbucket and a self-hosted server work the same way over HTTPS. Put
their host in hosts, and put the credential that their HTTPS git endpoint
expects in the value.
SSH on a paid account
A paid sandbox reaches any public host on any port, so git@github.com: URLs
work. SSH needs the private key inside the sandbox. So use a deploy key. A
deploy key belongs to one repository. It is read-only unless you allow write
access when you add it.
Pythonimport osfrom withruntime import Sandboxwith Sandbox.create() as sbx: sbx.files.write("/workspace/.ssh/id_ed25519", os.environ["DEPLOY_KEY"], mode=0o600) sbx.exec("git clone git@github.com:acme/private-app.git app", check=True, timeout_ms=300_000, env={"GIT_SSH_COMMAND": "ssh -o StrictHostKeyChecking=accept-new"})accept-new records GitHub's host key on first contact. Without it, SSH stops
to ask. If the sandbox has an allow list, add github.com:22 to its connect
rules (the network).
Faster clones for big repositories
| Need | Command |
|---|---|
| The latest commit only | git clone --depth 1 <url> |
| History, but no old file contents | git clone --filter=blob:none <url> |
| One directory of a monorepo | git clone --filter=blob:none --sparse <url>, then git sparse-checkout set <dir> |
The default 4 GiB disk has about 2.5 GiB free. Give a large repository more
space with diskMiB. To start every sandbox with the repository already there,
clone it once and keep a snapshot. New sandboxes
made from the snapshot need only git pull.
Related
- A sandbox for a coding agent
- Open pull requests with the GitHub CLI
- Secrets sandboxes never see
- Run Claude Code in a sandbox
- Egress control
Sources
Checked 25 October 2026.
- actions/checkout, git-auth-helper.ts:
the
AUTHORIZATION: basicheader built fromx-access-token:<token> - Managing deploy keys: one repository per key, read-only by default
- git in Ubuntu noble: 2.43.0
- Managing personal access tokens and fine-grained token permissions: "Only select repositories" and the "Contents" permission
- git clone and
git sparse-checkout:
--depth,--filter,--sparse
Facts on this page were checked on 25 October 2026.