runtime.git and runtime.file for that loop.
Prerequisites: complete Get started so Anthropic egress works. Extend the allowlist with your git host (below).
Allow git egress
If your policy is an allowlist (notallow_all), add GitHub (or your forge):
client.network_policies.add_rule(
os.environ["CLAUDE_NETWORK_POLICY_ID"],
destination="github.com",
port=443,
protocol="tcp",
description="GitHub HTTPS",
)
gravixlayer network-policy add-rule "$CLAUDE_NETWORK_POLICY_ID" \
--destination github.com \
--port 443 \
--protocol tcp \
--description "GitHub HTTPS"
auth_token= to runtime.git.clone, or store GITHUB_TOKEN on an Identity provider.
Full workflow: clone → CLAUDE.md → edit → diff
Uses the public octocat/Hello-World repo so the snippet runs without your org credentials.import os
import uuid
from gravixlayer import GravixLayer
TEMPLATE = os.environ["GRAVIXLAYER_CLAUDE_TEMPLATE"]
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_PATH = "/workspace/repo"
client = GravixLayer()
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
runtime = client.runtime.create(
template=TEMPLATE,
providers=[provider_id],
network_policy_ids=[policy_id],
env_vars={"ANTHROPIC_API_KEY": ANTHROPIC_API_KEY},
timeout=3600,
)
print("runtime_id:", runtime.runtime_id)
clone = runtime.git.clone(
url=CLONE_URL,
path=REPO_PATH,
branch=CLONE_BRANCH,
depth=1,
auth_token=os.environ.get("GITHUB_TOKEN") or None,
)
assert clone.success, clone.stderr or clone.stdout
runtime.file.write(
f"{REPO_PATH}/CLAUDE.md",
"# Project notes\n\n"
"- Prefer small, reviewable changes\n"
"- Do not commit secrets\n",
)
result = runtime.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"-p",
"Add a one-line README note that this repo was touched by Claude Code "
"in a Gravix Layer sandbox. Keep the change minimal.",
"--output-format",
"text",
],
working_dir=REPO_PATH,
timeout=1200,
)
print(result.stdout)
print("exit_code:", result.exit_code)
status = runtime.git.status(REPO_PATH)
print("git status:\n", status.stdout)
diff = runtime.run_cmd(
command="git",
args=["diff", "--stat"],
working_dir=REPO_PATH,
timeout=60,
)
print(diff.stdout)
runtime.kill()
# PROVIDER_ID + POLICY_ID must allow api.anthropic.com and github.com
RT=$(gravixlayer runtime create \
--template "$GRAVIXLAYER_CLAUDE_TEMPLATE" \
--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 exec "$RT" --timeout 180 -- \
git clone --depth 1 --branch master \
https://github.com/octocat/Hello-World.git /workspace/repo
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 one-line README note that this repo was touched by Claude Code.' \
--output-format text
gravixlayer runtime exec "$RT" --workdir /workspace/repo -- git status
gravixlayer runtime exec "$RT" --workdir /workspace/repo -- git diff --stat
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 one-line README note that this repo was touched by Claude Code.",
"--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 .
Download a changed file
content = runtime.file.download_file(f"{REPO_PATH}/README")
open("./README.downloaded", "wb").write(content)
Commit and push (optional)
Requires push credentials.runtime.git.push accepts username / password (token as password is common for HTTPS).
branch = "agent/readme-note"
assert runtime.git.create_branch(REPO_PATH, branch).success
assert runtime.git.checkout(REPO_PATH, branch).success
assert runtime.git.add(REPO_PATH, paths=["."]).success
assert runtime.git.commit(
REPO_PATH,
message="docs: note Claude Code sandbox run",
author_name="Claude on Gravix",
author_email="agents@example.com",
).success
token = os.environ["GITHUB_TOKEN"]
push = runtime.git.push(
REPO_PATH,
remote="origin",
username="x-access-token",
password=token,
)
assert push.success, push.stderr or push.stdout
Multi-step: plan then implement
import json
plan = runtime.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"--output-format",
"json",
"-p",
"Read the repo and produce a short plan to improve the README.",
],
working_dir=REPO_PATH,
timeout=900,
)
session_id = json.loads(plan.stdout)["session_id"]
runtime.run_cmd(
command="claude",
args=[
"--dangerously-skip-permissions",
"--resume",
session_id,
"-p",
"Implement step 1 only.",
],
working_dir=REPO_PATH,
timeout=1200,
)