> ## Documentation Index
> Fetch the complete documentation index at: https://code.storage/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# External Sandbox Integration

> Clone repositories into third-party sandboxes, such as Modal, E2B, and Daytona, with authenticated Git URLs.

Sandbox environments let you run code in isolated, temporary containers. Use Git as your source of
truth instead of provider-specific storage, such as volumes. Git gives you version history,
branches, and portability across sandbox providers.

Generate an authenticated Git URL with the Pierre SDK. Use the URL to clone the repository in your
sandbox.

Use this guide to connect a sandbox provider. To isolate agent writes and keep durable state, see
[Capture Agent State](https://code.storage/docs/guides/capture-agent-state.md).

> **Note:** Use a shallow, single-branch clone to reduce the clone time. This clone omits the commit
> history and other branches:

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git clone --depth 1 --single-branch <url> repo
```

## Quick start

Generate an authenticated Git URL and use it inside any sandbox:

Set `PIERRE_ORG` and `PIERRE_PRIVATE_KEY` as shown in the
[Quick Start](https://code.storage/docs/getting-started/quick-start.md).

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { Pierre } from '@pierre/sdk';
import { remoteUrl } from '@pierre/sdk/git';

const pierre = new Pierre(); // reads PIERRE_ORG and PIERRE_PRIVATE_KEY

// Read-only access
const readUrl = await remoteUrl(pierre, {
  repo_name: 'my-project',
  scopes: ['git:read'],
  ttl: 3600,
});

// Read-write access (for pushes)
const writeUrl = await remoteUrl(pierre, {
  repo_name: 'my-project',
  scopes: ['git:read', 'git:write'],
  ttl: 3600,
});
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from pierre import Pierre
from pierre.git import remote_url

pierre = Pierre()  # reads PIERRE_ORG and PIERRE_PRIVATE_KEY

# Read-only access
read_url = remote_url(
    pierre,
    repo_name="my-project",
    scopes=["git:read"],
    ttl=3600,
)

# Read-write access (for pushes)
write_url = remote_url(
    pierre,
    repo_name="my-project",
    scopes=["git:read", "git:write"],
    ttl=3600,
)
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import (
	"context"
	"fmt"
	"time"

	pierre "github.com/pierrecomputer/sdk/go"
	"github.com/pierrecomputer/sdk/go/git"
)

ctx := context.Background()
client, err := pierre.NewClient() // reads PIERRE_ORG and PIERRE_PRIVATE_KEY
if err != nil {
	return err
}

// Read-only access
readURL, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName: "my-project",
	Scopes:   []string{"git:read"},
	TTL:      time.Hour,
})
if err != nil {
	return err
}

// Read-write access (for pushes)
writeURL, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
	RepoName: "my-project",
	Scopes:   []string{"git:read", "git:write"},
	TTL:      time.Hour,
})
if err != nil {
	return err
}

fmt.Println(readURL, writeURL)
```

## Confine a sandbox to one session branch

Give the agent two short-lived URLs. Use a read-only normal URL for fetches. Use an ephemeral URL
for pushes. Use `ref_policies` to confine it to one branch.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
async function sessionRemotes(sessionBranch: string) {
  const readUrl = await remoteUrl(pierre, {
    repo_name: 'my-project',
    scopes: ['git:read'],
    ttl: 3600,
  });

  const sessionRef = `refs/namespaces/ephemeral/refs/heads/${sessionBranch}`;
  const writeUrl = await remoteUrl(pierre, {
    repo_name: 'my-project',
    kind: 'ephemeral',
    scopes: ['git:read', 'git:write'],
    ref_policies: [
      { pattern: sessionRef, ops: ['no-force-push'] },
      { pattern: '*', ops: ['no-push'] },
    ],
    ttl: 3600,
  });

  return { readUrl, writeUrl };
}
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
def session_remotes(session_branch: str) -> tuple[str, str]:
    read_url = remote_url(
        pierre,
        repo_name="my-project",
        scopes=["git:read"],
        ttl=3600,
    )

    session_ref = f"refs/namespaces/ephemeral/refs/heads/{session_branch}"
    write_url = remote_url(
        pierre,
        repo_name="my-project",
        kind="ephemeral",
        scopes=["git:read", "git:write"],
        ref_policies=[
            {"pattern": session_ref, "ops": ["no-force-push"]},
            {"pattern": "*", "ops": ["no-push"]},
        ],
        ttl=3600,
    )

    return read_url, write_url
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import "github.com/pierrecomputer/sdk/go/option"

func sessionRemotes(
	ctx context.Context,
	client *pierre.Client,
	sessionBranch string,
) (readURL, writeURL string, err error) {
	readURL, err = git.RemoteURL(ctx, client, git.RemoteURLParams{
		RepoName: "my-project",
		Scopes:   []string{"git:read"},
		TTL:      time.Hour,
	})
	if err != nil {
		return "", "", err
	}

	sessionRef := fmt.Sprintf("refs/namespaces/ephemeral/refs/heads/%s", sessionBranch)
	writeURL, err = git.RemoteURL(ctx, client, git.RemoteURLParams{
		RepoName: "my-project",
		Kind:     git.KindEphemeral,
		Scopes:   []string{"git:read", "git:write"},
		RefPolicies: []option.RefPolicy{
			{Pattern: sessionRef, Ops: []string{"no-force-push"}},
			{Pattern: "*", Ops: []string{"no-push"}},
		},
		TTL: time.Hour,
	})
	if err != nil {
		return "", "", err
	}

	return readURL, writeURL, nil
}
```

Set the normal URL as the fetch URL and the ephemeral URL as the push URL before the agent starts.
Pass the base commit SHA as `BASE_SHA`. Pass `sessionBranch` as `SESSION_BRANCH`.

```bash theme={"theme":{"light":"github-light","dark":"min-dark"}}
git clone --depth 1 --single-branch --no-checkout "$READ_URL" work
cd work
git fetch origin "$BASE_SHA"
git remote set-url --push origin "$WRITE_URL"
git switch -c "$SESSION_BRANCH" "$BASE_SHA"

# The agent changes files and creates commits.
git push origin "$SESSION_BRANCH"
```

The sandbox keeps one `origin` with separate URLs for fetches and pushes. A stray
`git push origin main` goes to the ephemeral namespace. The sandbox has no credential that can write
to a normal branch. The policy permits fast-forward pushes to this session branch only. It rejects
all other pushes.

See [Capture Agent State](https://code.storage/docs/guides/capture-agent-state.md) for the branch layout, and
[Ref Policies](https://code.storage/docs/repos/ref-policies.md) for the `ref_policies` rules.

## Modal

[Modal](https://modal.com) provides serverless cloud functions with persistent volumes. Clone
repositories into volumes, run code, and push changes back.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { ModalClient } from 'modal';

const modal = new ModalClient();

async function runInModalSandbox(repoName: string) {
  const url = await remoteUrl(pierre, {
    repo_name: repoName,
    scopes: ['git:read', 'git:write'],
    ttl: 3600,
  });

  // Create a Modal sandbox
  const app = await modal.apps.fromName('code-runner', { createIfMissing: true });
  const image = modal.images.fromRegistry('python:3.12-slim');
  const sb = await modal.sandboxes.create(app, image);

  // Shallow clone for speed
  await sb.exec(['git', 'clone', '--depth', '1', '--single-branch', url, '/code/repo']);
  await sb.exec(['git', 'config', '--global', 'user.email', 'agent@example.com']);
  await sb.exec(['git', 'config', '--global', 'user.name', 'AI Agent']);

  // Run tests or make changes
  const result = await sb.exec(['pytest', '/code/repo/tests']);
  console.log(await result.stdout.readText());

  // Commit and push changes
  await sb.exec(['git', '-C', '/code/repo', 'add', '.']);
  await sb.exec(['git', '-C', '/code/repo', 'commit', '-m', 'Update from sandbox']);
  await sb.exec(['git', '-C', '/code/repo', 'push']);

  await sb.terminate();
}
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import modal
import subprocess

app = modal.App("code-runner")
vol = modal.Volume.from_name("code-cache", create_if_missing=True)


@app.function(volumes={"/code": vol})
def run_in_sandbox(repo_name: str) -> None:
    url = remote_url(
        pierre,
        repo_name=repo_name,
        scopes=["git:read", "git:write"],
        ttl=3600,
    )

    # Shallow clone for speed
    subprocess.run(
        ["git", "clone", "--depth", "1", "--single-branch", url, "/code/repo"],
        check=True,
    )
    subprocess.run(
        ["git", "config", "--global", "user.email", "agent@example.com"],
        check=True,
    )
    subprocess.run(
        ["git", "config", "--global", "user.name", "AI Agent"],
        check=True,
    )

    # Run tests or make changes
    subprocess.run(["pytest", "/code/repo/tests"], check=True)

    # Commit and push changes
    subprocess.run(["git", "-C", "/code/repo", "add", "."], check=True)
    subprocess.run(
        ["git", "-C", "/code/repo", "commit", "-m", "Update from sandbox"],
        check=True,
    )
    subprocess.run(["git", "-C", "/code/repo", "push"], check=True)

    vol.commit()
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
func runInModalSandbox(
	ctx context.Context,
	client *pierre.Client,
	repoName string,
	run func(args ...string) error,
) error {
	// Read-write access (for pushes)
	url, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
		RepoName: repoName,
		Scopes:   []string{"git:read", "git:write"},
		TTL:      time.Hour,
	})
	if err != nil {
		return err
	}

	// Use your sandbox provider's API or shell to clone and run commands
	if err := run("git", "clone", "--depth", "1", "--single-branch", url, "/code/repo"); err != nil {
		return err
	}
	return run("git", "-C", "/code/repo", "push")
}
```

## E2B

[E2B](https://e2b.dev) provides cloud sandboxes for AI agents. Clone repositories, execute code, and
push results back to Code Storage.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { Sandbox } from 'e2b';

async function runAgentTask(repoName: string) {
  const url = await remoteUrl(pierre, {
    repo_name: repoName,
    scopes: ['git:read', 'git:write'],
    ttl: 3600,
  });

  const sandbox = await Sandbox.create();

  // Shallow clone for speed
  await sandbox.commands.exec(`git clone --depth 1 --single-branch ${url} /home/user/repo`);
  await sandbox.commands.exec('git config --global user.email "agent@example.com"');
  await sandbox.commands.exec('git config --global user.name "AI Agent"');

  // Run commands and make changes
  await sandbox.commands.exec('cd /home/user/repo && npm install');
  await sandbox.commands.exec('cd /home/user/repo && npm test');

  // Agent generates new code...
  await sandbox.commands.exec('echo "// Generated by AI" >> /home/user/repo/src/index.ts');

  // Commit and push
  await sandbox.commands.exec('cd /home/user/repo && git add .');
  await sandbox.commands.exec('cd /home/user/repo && git commit -m "Agent changes"');
  await sandbox.commands.exec('cd /home/user/repo && git push');

  await sandbox.kill();
}
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from e2b import Sandbox


def run_agent_task(repo_name: str) -> None:
    url = remote_url(
        pierre,
        repo_name=repo_name,
        scopes=["git:read", "git:write"],
        ttl=3600,
    )

    sandbox = Sandbox()

    # Shallow clone for speed
    sandbox.commands.run(f"git clone --depth 1 --single-branch {url} /home/user/repo")
    sandbox.commands.run('git config --global user.email "agent@example.com"')
    sandbox.commands.run('git config --global user.name "AI Agent"')

    # Run commands and make changes
    sandbox.commands.run("cd /home/user/repo && pip install -r requirements.txt")
    sandbox.commands.run("cd /home/user/repo && pytest")

    # Agent generates new code...
    sandbox.commands.run('echo "# Generated by AI" >> /home/user/repo/main.py')

    # Commit and push
    sandbox.commands.run("cd /home/user/repo && git add .")
    sandbox.commands.run('cd /home/user/repo && git commit -m "Agent changes"')
    sandbox.commands.run("cd /home/user/repo && git push")

    sandbox.kill()
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
func runAgentTask(
	ctx context.Context,
	client *pierre.Client,
	repoName string,
	run func(args ...string) error,
) error {
	// Generate a Git URL with read-write access
	url, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
		RepoName: repoName,
		Scopes:   []string{"git:read", "git:write"},
		TTL:      time.Hour,
	})
	if err != nil {
		return err
	}

	// In your sandbox shell:
	if err := run("git", "clone", "--depth", "1", "--single-branch", url, "/home/user/repo"); err != nil {
		return err
	}
	return run("git", "-C", "/home/user/repo", "push")
}
```

## Daytona

[Daytona](https://daytona.io) provides secure sandboxes for AI agents. Use Daytona's built-in Git
API to clone repositories. Run commands and push changes back.

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
import { Daytona } from '@daytonaio/sdk';

const daytona = new Daytona();

async function runInDaytona(repoName: string) {
  const url = await remoteUrl(pierre, {
    repo_name: repoName,
    scopes: ['git:read', 'git:write'],
    ttl: 3600,
  });

  // Create sandbox
  const sandbox = await daytona.create();

  // Clone with Daytona's Git API
  await sandbox.git.clone(url, '/home/daytona/repo');

  // Run commands
  await sandbox.process.executeCommand('npm install', '/home/daytona/repo');
  await sandbox.process.executeCommand('npm test', '/home/daytona/repo');

  // Use the Git API to change and commit files
  await sandbox.git.add('/home/daytona/repo', ['.']);
  await sandbox.git.commit(
    '/home/daytona/repo',
    'Update from sandbox',
    'AI Agent',
    'agent@example.com',
  );
  await sandbox.git.push('/home/daytona/repo');

  await sandbox.delete();
}
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from daytona_sdk import Daytona

daytona = Daytona()


def run_in_daytona(repo_name: str) -> None:
    url = remote_url(
        pierre,
        repo_name=repo_name,
        scopes=["git:read", "git:write"],
        ttl=3600,
    )

    # Create sandbox
    sandbox = daytona.create()

    # Clone with Daytona's Git API
    sandbox.git.clone(url, "/home/daytona/repo")

    # Run commands
    sandbox.process.exec("pip install -r requirements.txt", cwd="/home/daytona/repo")
    sandbox.process.exec("pytest", cwd="/home/daytona/repo")

    # Use the Git API to change and commit files
    sandbox.git.add("/home/daytona/repo", ["."])
    sandbox.git.commit("/home/daytona/repo", "Update from sandbox", "AI Agent", "agent@example.com")
    sandbox.git.push("/home/daytona/repo")

    sandbox.delete()
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
func runInDaytona(
	ctx context.Context,
	client *pierre.Client,
	repoName string,
	run func(args ...string) error,
) error {
	// Generate a Git URL with read-write access
	url, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
		RepoName: repoName,
		Scopes:   []string{"git:read", "git:write"},
		TTL:      time.Hour,
	})
	if err != nil {
		return err
	}

	// Use the Daytona Go SDK if available, or run git commands in the sandbox shell.
	if err := run("git", "clone", url, "/home/daytona/repo"); err != nil {
		return err
	}
	return run("git", "-C", "/home/daytona/repo", "push")
}
```

## Provider resilience

Many teams use multiple sandbox providers to improve resilience. Git keeps your code portable across
providers. If one provider has an outage or no capacity, start a sandbox elsewhere. Then clone the
same repository:

```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
async function runWithFallback(repoName: string, task: () => Promise<void>) {
  const url = await remoteUrl(pierre, {
    repo_name: repoName,
    scopes: ['git:read', 'git:write'],
    ttl: 3600,
  });

  try {
    // Try primary provider
    const e2b = await Sandbox.create();
    await e2b.commands.exec(`git clone --depth 1 --single-branch ${url} /repo`);
    await task();
  } catch (err) {
    // Fallback to secondary provider
    const daytona = new Daytona();
    const sandbox = await daytona.create();
    await sandbox.git.clone(url, '/repo');
    await task();
  }
}
```

```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
from collections.abc import Callable


def run_with_fallback(repo_name: str, task: Callable[[], None]) -> None:
    url = remote_url(
        pierre,
        repo_name=repo_name,
        scopes=["git:read", "git:write"],
        ttl=3600,
    )

    try:
        # Try primary provider
        e2b = Sandbox()
        e2b.commands.run(f"git clone --depth 1 --single-branch {url} /repo")
        task()
    except Exception:
        # Fallback to secondary provider
        daytona = Daytona()
        sandbox = daytona.create()
        sandbox.git.clone(url, "/repo")
        task()
```

```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
func runWithFallback(
	ctx context.Context,
	client *pierre.Client,
	repoName string,
	task func(url string) error,
) error {
	url, err := git.RemoteURL(ctx, client, git.RemoteURLParams{
		RepoName: repoName,
		Scopes:   []string{"git:read", "git:write"},
		TTL:      time.Hour,
	})
	if err != nil {
		return err
	}

	// Try primary provider...
	if err := task(url); err != nil {
		// Use the same URL for the secondary provider.
		return task(url)
	}
	return nil
}
```

Your code stays safe in Code Storage regardless of which sandbox runs it.

## Security considerations

* **Short TTLs**: Use the shortest TTL practical for your use case. Sandbox sessions are typically
  short-lived. The token of a remote URL lives 15 minutes by default and 1 hour at most.
* **Minimal permissions**: Grant `git:read` only unless the sandbox needs to push changes.
* **Agent state**: Store experimental or untrusted work on ephemeral branches. See
  [Capture Agent State](https://code.storage/docs/guides/capture-agent-state.md).
* **Audit commits**: Review sandbox changes before you merge them into protected branches.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.