> ## 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.

# Connect a Sandbox

> 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 Code Storage 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
[Store Session State](/docs/guides/session-state).

> **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:

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  import { GitStorage } from '@pierre/storage';

  const store = new GitStorage({
    name: 'your-org',
    key: process.env.PIERRE_PRIVATE_KEY,
  });

  const repo = await store.findOne({ id: 'my-project' });

  // Read-only access
  const readUrl = await repo.getRemoteURL({
    permissions: ['git:read'],
    ttl: 3600,
  });

  // Read-write access (for pushes)
  const writeUrl = await repo.getRemoteURL({
    permissions: ['git:read', 'git:write'],
    ttl: 3600,
  });
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  import os
  from pierre_storage import GitStorage

  storage = GitStorage({
      "name": "your-org",
      "key": os.environ["PIERRE_PRIVATE_KEY"],
  })

  repo = await storage.find_one(id="my-project")

  # Read-only access
  read_url = await repo.get_remote_url(
      permissions=["git:read"],
      ttl=3600,
  )

  # Read-write access (for pushes)
  write_url = await repo.get_remote_url(
      permissions=["git:read", "git:write"],
      ttl=3600,
  )
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  ctx := context.Background()

  // Find the repository
  repo, err := client.FindOne(ctx, storage.FindOneOptions{ID: "my-project"})

  // Read-only access
  readURL, err := repo.RemoteURL(ctx, storage.RemoteURLOptions{
  	Permissions: []storage.Permission{storage.PermissionGitRead},
  	TTL:         time.Hour,
  })

  // Read-write access (for pushes)
  writeURL, err := repo.RemoteURL(ctx, storage.RemoteURLOptions{
  	Permissions: []storage.Permission{storage.PermissionGitRead, storage.PermissionGitWrite},
  	TTL:         time.Hour,
  })
  ```
</CodeGroup>

## 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 `refPolicies` to confine it to one branch.

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  const readUrl = await repo.getRemoteURL({
    permissions: ['git:read'],
    ttl: 3600,
  });

  const sessionRef = `refs/namespaces/ephemeral/refs/heads/${sessionBranch}`;
  const writeUrl = await repo.getEphemeralRemoteURL({
    permissions: ['git:read', 'git:write'],
    refPolicies: [
      { ops: ['no-force-push'], pattern: sessionRef },
      { ops: ['no-push'], pattern: '*' },
    ],
    ttl: 3600,
  });
  ```

  ```python Python theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  read_url = await repo.get_remote_url(
      permissions=["git:read"],
      ttl=3600,
  )

  session_ref = f"refs/namespaces/ephemeral/refs/heads/{session_branch}"
  write_url = await repo.get_ephemeral_remote_url(
      permissions=["git:read", "git:write"],
      ref_policies=[
          {"ops": ["no-force-push"], "pattern": session_ref},
          {"ops": ["no-push"], "pattern": "*"},
      ],
      ttl=3600,
  )
  ```

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  readURL, err := repo.RemoteURL(ctx, storage.RemoteURLOptions{
  	Permissions: []storage.Permission{storage.PermissionGitRead},
  	TTL:         time.Hour,
  })

  sessionRef := fmt.Sprintf("refs/namespaces/ephemeral/refs/heads/%s", sessionBranch)
  writeURL, err := repo.EphemeralRemoteURL(ctx, storage.RemoteURLOptions{
  	Permissions: []storage.Permission{
  		storage.PermissionGitRead,
  		storage.PermissionGitWrite,
  	},
  	RefPolicies: storage.RefPolicyList{
  		{Ops: storage.Ops{storage.OpNoForcePush}, Pattern: sessionRef},
  		{Ops: storage.Ops{storage.OpNoPush}, Pattern: "*"},
  	},
  	TTL: time.Hour,
  })
  ```
</CodeGroup>

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 [Store Session State](/docs/guides/session-state) for the branch layout, and
[Ref Policies](/docs/guides/ref-policies) for the `refPolicies` rules.

## Modal

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

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  import { ModalClient } from 'modal';
  import { GitStorage } from '@pierre/storage';

  const modal = new ModalClient();
  const store = new GitStorage({
    name: 'your-org',
    key: process.env.PIERRE_PRIVATE_KEY,
  });

  async function runInModalSandbox(repoId: string) {
    const repo = await store.findOne({ id: repoId });
    const url = await repo.getRemoteURL({
      permissions: ['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
  import os
  from pierre_storage import GitStorage

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

  storage = GitStorage({
      "name": "your-org",
      "key": os.environ["PIERRE_PRIVATE_KEY"],
  })

  @app.function(volumes={"/code": vol})
  async def run_in_sandbox(repo_id: str):
      repo = await storage.find_one(id=repo_id)
      url = await repo.get_remote_url(
          permissions=["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"}}
  // Generate a Git URL, then use your sandbox provider's API or shell to clone and run commands
  repo, err := client.FindOne(context.Background(), storage.FindOneOptions{ID: "my-project"})

  // Read-write access (for pushes)
  url, err := repo.RemoteURL(context.Background(), storage.RemoteURLOptions{
  	Permissions: []storage.Permission{storage.PermissionGitRead, storage.PermissionGitWrite},
  	TTL:         time.Hour,
  })

  // Example shell commands inside a sandbox:
  // exec.Command("git", "clone", "--depth", "1", "--single-branch", url, "/code/repo").Run()
  // exec.Command("git", "-C", "/code/repo", "push").Run()
  ```
</CodeGroup>

## E2B

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

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  import { Sandbox } from 'e2b';
  import { GitStorage } from '@pierre/storage';

  const store = new GitStorage({
    name: 'your-org',
    key: process.env.PIERRE_PRIVATE_KEY,
  });

  async function runAgentTask(repoId: string) {
    const repo = await store.findOne({ id: repoId });
    const url = await repo.getRemoteURL({
      permissions: ['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"}}
  import os
  from e2b import Sandbox
  from pierre_storage import GitStorage

  storage = GitStorage({
      "name": "your-org",
      "key": os.environ["PIERRE_PRIVATE_KEY"],
  })

  async def run_agent_task(repo_id: str):
      repo = await storage.find_one(id=repo_id)
      url = await repo.get_remote_url(
          permissions=["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"}}
  // Find the repository
  repo, err := client.FindOne(context.Background(), storage.FindOneOptions{ID: "my-project"})

  // Generate a Git URL with read-write access
  url, err := repo.RemoteURL(context.Background(), storage.RemoteURLOptions{
  	Permissions: []storage.Permission{storage.PermissionGitRead, storage.PermissionGitWrite},
  	TTL:         time.Hour,
  })

  // In your sandbox shell:
  // git clone --depth 1 --single-branch <url> /home/user/repo
  // git -C /home/user/repo push
  ```
</CodeGroup>

## 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.

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

  const store = new GitStorage({
    name: 'your-org',
    key: process.env.PIERRE_PRIVATE_KEY,
  });

  const daytona = new Daytona();

  async function runInDaytona(repoId: string) {
    const repo = await store.findOne({ id: repoId });
    const url = await repo.getRemoteURL({
      permissions: ['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"}}
  import os
  from daytona_sdk import Daytona
  from pierre_storage import GitStorage

  storage = GitStorage({
      "name": "your-org",
      "key": os.environ["PIERRE_PRIVATE_KEY"],
  })

  daytona = Daytona()

  async def run_in_daytona(repo_id: str):
      repo = await storage.find_one(id=repo_id)
      url = await repo.get_remote_url(
          permissions=["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"}}
  // Find the repository
  repo, err := client.FindOne(context.Background(), storage.FindOneOptions{ID: "my-project"})

  // Generate a Git URL with read-write access
  url, err := repo.RemoteURL(context.Background(), storage.RemoteURLOptions{
  	Permissions: []storage.Permission{storage.PermissionGitRead, storage.PermissionGitWrite},
  	TTL:         time.Hour,
  })

  // Use the Daytona Go SDK if available, or run git commands in the sandbox shell.
  ```
</CodeGroup>

## 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:

<CodeGroup>
  ```typescript TypeScript theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  async function runWithFallback(repoId: string, task: () => Promise<void>) {
    const repo = await store.findOne({ id: repoId });
    const url = await repo.getRemoteURL({
      permissions: ['git:read', 'git:write'],
      ttl: 3600,
    });

    try {
      // Try primary provider
      const e2b = await E2BSandbox.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"}}
  async def run_with_fallback(repo_id: str, task):
      repo = await storage.find_one(id=repo_id)
      url = await repo.get_remote_url(
          permissions=["git:read", "git:write"],
          ttl=3600,
      )

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

  ```go Go theme={null} theme={"theme":{"light":"github-light","dark":"min-dark"}}
  func runWithFallback(ctx context.Context, repoID string, task func() error) error {
  	repo, err := client.FindOne(ctx, storage.FindOneOptions{ID: repoID})
  	if err != nil {
  		return err
  	}
  	url, err := repo.RemoteURL(ctx, storage.RemoteURLOptions{
  		Permissions: []storage.Permission{storage.PermissionGitRead, storage.PermissionGitWrite},
  		TTL:         time.Hour,
  	})
  	if err != nil {
  		return err
  	}

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

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.
* **Minimal permissions**: Grant `git:read` only unless the sandbox needs to push changes.
* **Agent state**: Store experimental or untrusted work on ephemeral branches. See
  [Store Session State](/docs/guides/session-state).
* **Audit commits**: Review sandbox changes before you merge them into protected branches.
