# How to have an AI agent set up any repository in a sandbox Clone the repo into a fresh microVM, let the agent install and test until the suite passes, then snapshot the machine for later tasks. **On Runtime a setup that worked once becomes the starting point of every later task.** A snapshot keeps the sandbox's files, memory and running processes for 1 to 365 days, so the next task starts with dependencies installed, services up and the test suite known to pass. Onboarding 40 repositories a month costs $7.20 at Runtime's rates, snapshots included, worked out below ([pricing](/docs/pricing), 25 September 2026). ## What "onboarding a repository" means for an agent A person new to a codebase reads the README, installs the toolchain, fights the one dependency that needs a system library, and runs the tests until they pass. An agent does the same work, faster and less carefully. Two things make it safe and useful: - **A machine of its own.** The agent runs `curl | sh` installers, `sudo apt-get`, and whatever the README says. None of it should touch your laptop or CI runners. - **A record of what worked.** The commands that led to a green test run are the setup script nobody wrote down. Keep them, and keep the machine. Every sandbox has Ubuntu 24.04 with Python 3.12, Node.js 24, Bun, git and gcc, and `sudo` works, so most projects need only their own dependencies ([the sandbox environment](/docs/sandbox-environment)). ## The core loop Clone, show the agent the repository, run the commands it chooses, and stop when it says the tests pass. Keep every command, and snapshot the result: ```ts check import { Runtime } from "withruntime"; // Your model: given everything so far, the next shell command, or null when the tests pass. type NextCommand = (transcript: string) => Promise; const runtime = new Runtime(); export async function onboard(repoUrl: string, next: NextCommand, maxSteps = 30) { const sbx = await runtime.sandboxes.create({ diskMiB: 16_384, timeoutSeconds: 3600, labels: { job: "onboard" }, }); await sbx.exec(["git", "clone", "--depth", "1", repoUrl, "/workspace/repo"], { check: true, timeoutMs: 300_000, }); const look = await sbx.exec("ls -a; head -c 6000 README* 2>/dev/null", { cwd: "/workspace/repo", }); let transcript = look.stdout; const steps: string[] = []; for (let i = 0; i < maxSteps; i++) { const command = await next(transcript); if (command === null) break; const run = await sbx.exec(command, { cwd: "/workspace/repo", timeoutMs: 900_000 }); if (run.exitCode === 0) steps.push(command); const tail = (run.stdout + run.stderr).slice(-4000); transcript += `\n$ ${command}\nexit ${run.exitCode}${run.timedOut ? " (timed out)" : ""}\n${tail}`; } await sbx.files.write( "/workspace/setup.sh", `set -e\ncd /workspace/repo\n${steps.join("\n")}\n`, { mode: 0o755, }, ); const snapshot = await sbx.snapshot({ name: "repo-ready", retentionDays: 30 }); await sbx.stop(); return { snapshotId: snapshot.id, steps }; } ``` ```python check from withruntime import Runtime runtime = Runtime() def onboard(repo_url: str, next_command, max_steps: int = 30) -> dict: """next_command(transcript) returns the next shell command, or None when the tests pass.""" sbx = runtime.sandboxes.create(disk_mib=16_384, timeout_seconds=3600, labels={"job": "onboard"}) sbx.exec(["git", "clone", "--depth", "1", repo_url, "/workspace/repo"], check=True, timeout_ms=300_000) transcript = sbx.exec("ls -a; head -c 6000 README* 2>/dev/null", cwd="/workspace/repo").stdout steps = [] for _ in range(max_steps): command = next_command(transcript) if command is None: break run = sbx.exec(command, cwd="/workspace/repo", timeout_ms=900_000) if run.exit_code == 0: steps.append(command) tail = (run.stdout + run.stderr)[-4000:] transcript += f"\n$ {command}\nexit {run.exit_code}{' (timed out)' if run.timed_out else ''}\n{tail}" sbx.files.write("/workspace/setup.sh", "set -e\ncd /workspace/repo\n" + "\n".join(steps) + "\n", mode=0o755) snapshot = sbx.snapshot(name="repo-ready", retention_days=30) sbx.stop() return {"snapshot_id": snapshot["id"], "steps": steps} ``` - The clone is an array command, so a repository URL cannot carry shell syntax. For a private repository, upload your local checkout with `sbx.files.upload("./repo", "/workspace/repo")` instead, and no token enters the sandbox. - A command over 60 seconds streams, and its result keeps everything it printed; the loop keeps only the tail, so a noisy install cannot flood the model's context. - Only commands that exited 0 go into `setup.sh`. Review it before you run it anywhere else. ## Start every later task from the working machine The snapshot is the repository, installed and tested. A coding task, an eval or a review creates a copy from it: ```ts check import { Runtime } from "withruntime"; const runtime = new Runtime(); export async function taskFrom(snapshotId: string, testCommand: string) { await using sbx = await runtime.sandboxes.create({ snapshot: snapshotId, labels: { job: "task" }, }); await sbx.exec("git pull --ff-only", { cwd: "/workspace/repo", timeoutMs: 120_000 }); const tests = await sbx.exec(testCommand, { cwd: "/workspace/repo", timeoutMs: 900_000 }); return tests.exitCode === 0; // still green? then hand the sandbox to the agent } ``` Several agents at once? `fork({ count })` copies a running sandbox into 1 to 10 running sandboxes, answered together, each with its own disk ([sandbox forks](/glossary/sandbox-fork)). ## Snapshot or image? | Keep it as | What it holds | Best when | Charged | | -------------- | ------------------------------------------ | ---------------------------------------------- | --------------------------------------- | | A snapshot | Files, memory and running processes | Services should be up the moment a task starts | $0.08/GB/month on bytes it alone stores | | A custom image | Files, rebuilt from a recipe or Dockerfile | The setup should be reviewed and versioned | $0.08/GB/month on the whole image file | | Neither | Nothing | One-off tasks on small repositories | Only the running time | `setup.sh` is the bridge between the two: once reviewed, its commands become the `RUN` lines of a Dockerfile that `runtime.images.build` turns into a numbered image version ([custom images](/docs/images)). ## What repository onboarding needs | Need | How Runtime covers it | | --------------------------------- | ---------------------------------------------------------------------------- | | Freedom to install anything | `sudo` inside the guest; root cannot change network, CPU, memory or cost | | Room for dependencies | `diskMiB` up to what the project needs; the default 4 GiB has about 2.5 free | | Databases the tests expect | `sudo enable-docker`, then Docker Compose in the same sandbox | | Long installs | `timeoutMs` up to 24 hours per command | | Keeping what worked | Snapshots, 1 to 365 days; `setup.sh` from the commands that exited 0 | | Several repositories at once | 100 sandboxes at once on a paid account to start | | Your code kept from other tenants | A Firecracker microVM with its own kernel per sandbox | ## What it costs Take 40 repositories a month. Each onboarding keeps a 2 vCPU, 4 GiB sandbox running for 20 minutes, with installs and tests using 1.2 vCPUs on average, and each snapshot keeps 2 GB of its own for the month: ``` CPU: 40 × 20 min / 60 × 1.2 vCPU × $0.025 = $0.40 Memory: 40 × 20 min / 60 × 4 GiB × $0.0075 = $0.40 Snapshots: 40 × 2 GB × $0.08 = $6.40 Total: $7.20 ``` A snapshot is charged on the bytes it alone stores; a block two of your snapshots share counts once ([pricing](/docs/pricing#snapshots-images-and-volumes)). New accounts get 50 free sandbox hours, no card. The trial reaches ports 80 and 443, so clone over HTTPS there. ## Start ```bash no-run npx withruntime sandbox run --trial --keep -- git --version ``` The first run prints a link to approve in your browser. `runtime sandbox ssh ` then opens a shell in the sandbox the agent set up ([SSH and editors](/docs/editors)). Related: [a coding agent sandbox](/use-cases/coding-agent-sandbox), [legacy code migration](/use-cases/legacy-code-migration), [sandbox snapshots](/glossary/sandbox-snapshot), [per-user dev environments](/use-cases/per-user-dev-environments), [agent evals and SWE-bench](/use-cases/agent-evals-and-swe-bench). Facts on this page were checked on 25 September 2026.