CLAUDE.md → run Claude → inspect the diff → (optional) commit and push. Use sandbox.git for git and sandbox.run_cmd for claude.
Complete Get started first. Your network policy must allow Anthropic and your git host.
Allow GitHub (if your policy is an allowlist)
ReuseCLAUDE_NETWORK_POLICY_ID from Get started, then add GitHub:
gravixlayer network-policy add-rule "$CLAUDE_NETWORK_POLICY_ID" \
--destination github.com \
--port 443 \
--protocol tcp \
--description "GitHub HTTPS"
GIT_AUTH_TOKEN (or GRAVIXLAYER_GIT_TOKEN for the CLI). Pass it on every clone / fetch / pull / push — tokens are not stored in the checkout.
End-to-end: clone, edit, review
Uses the public octocat/Hello-World repo so you can run without org credentials. Override withGIT_CLONE_URL / GIT_BRANCH for your own repo.
import os
import uuid
from gravixlayer import GravixLayer
ANTHROPIC_API_KEY = os.environ["ANTHROPIC_API_KEY"]
CLONE_URL = os.environ.get("GIT_CLONE_URL", "https://github.com/octocat/Hello-World.git")
CLONE_BRANCH = os.environ.get("GIT_BRANCH", "master")
REPO = "/workspace/repo"
GIT_TOKEN = os.environ.get("GIT_AUTH_TOKEN") or os.environ.get("GITHUB_TOKEN")
client = GravixLayer() # defaults to cloud="aws", region="us-east-1"
provider_id = os.environ.get("ANTHROPIC_PROVIDER_ID")
policy_id = os.environ.get("CLAUDE_NETWORK_POLICY_ID")
if not provider_id:
provider = client.identity.providers.create(
name=f"anthropic-{uuid.uuid4().hex[:8]}",
provider_type="api_key",
secrets=[{"key": "ANTHROPIC_API_KEY", "value": ANTHROPIC_API_KEY}],
)
provider_id = provider.id
if not policy_id:
policy = client.network_policies.create(
name=f"claude-git-{uuid.uuid4().hex[:8]}",
egress_mode="allowlist",
rules=[
{"destination": "api.anthropic.com", "port": 443, "protocol": "tcp"},
{"destination": "github.com", "port": 443, "protocol": "tcp"},
],
)
policy_id = policy.id
sandbox = client.runtime.create(
template="claude",
providers=[provider_id],
network_policy_ids=[policy_id],
env_vars={"ANTHROPIC_API_KEY": ANTHROPIC_API_KEY},
timeout=3600,
)
print("runtime_id:", sandbox.runtime_id)
clone = sandbox.git.clone(
url=CLONE_URL,
path=REPO,
branch=CLONE_BRANCH,
depth=1,
auth_token=GIT_TOKEN,
)
print("clone:", clone.success, clone.exit_code)
if not clone.success:
raise SystemExit(clone.stderr or clone.stdout)
sandbox.file.write(
f"{REPO}/CLAUDE.md",
"# Project notes\n\n- Prefer small, reviewable changes\n- Do not commit secrets\n",
)
result = sandbox.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"-p",
"Add a single line to the README noting that Claude Code ran in a Gravix sandbox. "
"Keep the change minimal. Do not commit.",
"--output-format",
"text",
],
working_dir=REPO,
timeout=1200,
)
print(result.stdout)
print("status:\n", sandbox.git.status(REPO).stdout)
print(
sandbox.run_cmd(
command="git",
args=["diff", "--stat"],
working_dir=REPO,
timeout=60,
).stdout
)
# Pull a changed file to your laptop (Hello-World uses a file named README)
local = sandbox.file.download_file(f"{REPO}/README")
open("./README.downloaded", "wb").write(local)
print("wrote ./README.downloaded", len(local), "bytes")
# Optional: push a branch (needs write access on the remote)
if os.environ.get("GIT_ENABLE_PUSH") == "1" and GIT_TOKEN:
branch = f"agent/readme-{uuid.uuid4().hex[:6]}"
sandbox.git.create_branch(REPO, branch)
sandbox.git.checkout(REPO, branch)
sandbox.git.add(REPO, paths=["."])
sandbox.git.commit(
REPO,
"docs: note Claude Code sandbox run",
author_name="Claude on Gravix",
author_email="agents@example.com",
)
push = sandbox.git.push(REPO, remote="origin", refspec=branch, auth_token=GIT_TOKEN)
print("push:", push.success, push.exit_code, push.stderr or push.stdout)
sandbox.kill()
import { writeFile } from 'node:fs/promises';
import { GravixLayer } from 'gravixlayer';
const ANTHROPIC_API_KEY = process.env['ANTHROPIC_API_KEY']!;
const CLONE_URL = process.env['GIT_CLONE_URL'] ?? 'https://github.com/octocat/Hello-World.git';
const CLONE_BRANCH = process.env['GIT_BRANCH'] ?? 'master';
const REPO = '/workspace/repo';
const GIT_TOKEN = process.env['GIT_AUTH_TOKEN'] ?? process.env['GITHUB_TOKEN'];
const client = new GravixLayer(); // defaults to cloud="aws", region="us-east-1"
let providerId = process.env['ANTHROPIC_PROVIDER_ID'];
let policyId = process.env['CLAUDE_NETWORK_POLICY_ID'];
if (!providerId) {
const provider = await client.identity.providers.create(`anthropic-${Date.now()}`, {
providerType: 'api_key',
secrets: [{ key: 'ANTHROPIC_API_KEY', value: ANTHROPIC_API_KEY }],
});
providerId = provider.id;
}
if (!policyId) {
const policy = await client.networkPolicies.create(`claude-git-${Date.now()}`, {
egressMode: 'allowlist',
rules: [
{ destination: 'api.anthropic.com', port: 443, protocol: 'tcp' },
{ destination: 'github.com', port: 443, protocol: 'tcp' },
],
});
policyId = policy.id;
}
const sandbox = await client.runtime.create({
template: 'claude',
providers: [providerId],
networkPolicyIds: [policyId],
envVars: { ANTHROPIC_API_KEY },
timeout: 3600,
});
console.log('runtime_id:', sandbox.runtimeId);
const clone = await sandbox.git.clone(CLONE_URL, REPO, {
branch: CLONE_BRANCH,
depth: 1,
authToken: GIT_TOKEN,
});
console.log('clone:', clone.success, clone.exitCode);
if (!clone.success) {
throw new Error(clone.stderr || clone.stdout);
}
await sandbox.file.write(
`${REPO}/CLAUDE.md`,
'# Project notes\n\n- Prefer small, reviewable changes\n- Do not commit secrets\n',
);
const result = await sandbox.runCmd('claude', {
args: [
'--dangerously-skip-permissions',
'-p',
'Add a single line to the README noting that Claude Code ran in a Gravix sandbox. Keep the change minimal. Do not commit.',
'--output-format',
'text',
],
workingDir: REPO,
timeout: 1200,
});
console.log(result.stdout);
console.log('status:\n', (await sandbox.git.status(REPO)).stdout);
console.log(
(
await sandbox.runCmd('git', {
args: ['diff', '--stat'],
workingDir: REPO,
timeout: 60,
})
).stdout,
);
const local = await sandbox.file.download(`${REPO}/README`);
await writeFile('./README.downloaded', local);
console.log('wrote ./README.downloaded', local.byteLength, 'bytes');
if (process.env['GIT_ENABLE_PUSH'] === '1' && GIT_TOKEN) {
const branch = `agent/readme-${Date.now().toString(36).slice(-6)}`;
await sandbox.git.createBranch(REPO, branch);
await sandbox.git.checkout(REPO, branch);
await sandbox.git.add(REPO, ['.']);
await sandbox.git.commit(REPO, 'docs: note Claude Code sandbox run', {
authorName: 'Claude on Gravix',
authorEmail: 'agents@example.com',
});
const push = await sandbox.git.push(REPO, { remote: 'origin', refspec: branch, authToken: GIT_TOKEN });
console.log('push:', push.success, push.exitCode, push.stderr || push.stdout);
}
await sandbox.kill();
# Requires ANTHROPIC_PROVIDER_ID + CLAUDE_NETWORK_POLICY_ID (with github.com).
# Optional: GRAVIXLAYER_GIT_TOKEN for private remotes / push.
RT=$(gravixlayer runtime create \
--template claude \
--provider "$ANTHROPIC_PROVIDER_ID" \
--network-policy "$CLAUDE_NETWORK_POLICY_ID" \
--env "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \
--timeout 3600 \
--wait \
--output json | jq -r '.runtime_id')
gravixlayer runtime git clone "$RT" \
"${GIT_CLONE_URL:-https://github.com/octocat/Hello-World.git}" \
--target-dir /workspace/repo \
--branch "${GIT_BRANCH:-master}" \
--depth 1
cat <<'EOF' | gravixlayer runtime files write "$RT" /workspace/repo/CLAUDE.md
# Project notes
- Prefer small, reviewable changes
- Do not commit secrets
EOF
gravixlayer runtime exec "$RT" --timeout 1200 --workdir /workspace/repo -- \
claude --dangerously-skip-permissions \
-p 'Add a single line to the README noting Claude Code ran in a Gravix sandbox. Do not commit.' \
--output-format text
gravixlayer runtime git status "$RT" --path /workspace/repo
gravixlayer runtime exec "$RT" --workdir /workspace/repo -- git diff --stat
gravixlayer runtime files download "$RT" /workspace/repo/README ./README.downloaded
# Optional push (needs write access + GRAVIXLAYER_GIT_TOKEN):
# BRANCH="agent/readme-$(openssl rand -hex 3)"
# gravixlayer runtime git branch-create "$RT" "$BRANCH" --path /workspace/repo
# gravixlayer runtime git checkout "$RT" "$BRANCH" --path /workspace/repo
# gravixlayer runtime git add "$RT" --path /workspace/repo
# gravixlayer runtime git commit "$RT" --path /workspace/repo \
# -m "docs: note Claude Code sandbox run" \
# --author-name "Claude on Gravix" --author-email "agents@example.com"
# gravixlayer runtime git push "$RT" --path /workspace/repo \
# --remote origin --refspec "$BRANCH"
gravixlayer runtime kill "$RT" -y
API="https://api.gravixlayer.ai/v1"
AUTH=(-H "Authorization: Bearer $GRAVIXLAYER_API_KEY" -H "Content-Type: application/json")
# RT must already be running with Anthropic + github.com egress
curl -sS -X POST "$API/agents/runtime/$RT/git/clone" "${AUTH[@]}" \
-d '{
"url": "https://github.com/octocat/Hello-World.git",
"path": "/workspace/repo",
"branch": "master",
"depth": 1
}' | jq .
curl -sS -X POST "$API/agents/runtime/$RT/files/write" "${AUTH[@]}" \
-d '{
"path": "/workspace/repo/CLAUDE.md",
"content": "# Project notes\n\n- Prefer small changes\n"
}' | jq .
curl -sS -X POST "$API/agents/runtime/$RT/commands/run" "${AUTH[@]}" \
-d '{
"command": "claude",
"args": [
"--dangerously-skip-permissions",
"-p", "Add a single line to the README. Do not commit.",
"--output-format", "text"
],
"working_dir": "/workspace/repo",
"timeout": 1200000
}' | jq .
curl -sS -X POST "$API/agents/runtime/$RT/git/status" "${AUTH[@]}" \
-d '{"repository_path": "/workspace/repo"}' | jq .
GIT_ENABLE_PUSH=1 (and a token with write access) to exercise the push path in the Python example.