Sandcastle integration
Sandcastle does the orchestration. Gibil does the box. Run Sandcastle agent loops on a real VM instead of a local container.
Sandcastle is a TypeScript library for orchestrating AI coding agents in isolated sandboxes — agent runs N iterations until it emits a completion signal, commits accumulate on a branch, and the sandbox is torn down. Out of the box it ships providers for Docker, Podman, Vercel sandbox, Daytona, and a no-sandbox passthrough.
@gibil/sandcastle-provider is the first community-maintained provider: a real Hetzner VM with root, Docker-in-Docker, real systemd, ~$0.008/hr at Hetzner list price, with a TTL. Same run() call, same hooks, same agent — you just swap the sandbox. Sandcastle keeps the agent loop; Gibil becomes the box it runs on.
When to use it
| You want… | Pick |
|---|---|
| Fastest possible iteration on your laptop, you trust the agent | Sandcastle + docker() |
| Cloud isolation, but you're already on Vercel | Sandcastle + vercel() |
| Cloud isolation, full Linux root, no vendor lock-in, cheap | Sandcastle + gibil() |
| Long-running or CPU-heavy runs (multi-hour migrations, builds) | Sandcastle + gibil() — VMs are cheap to keep alive |
Install
npm install --save-dev @gibil/sandcastle-provider @ai-hero/sandcastle
npm install -g gibil
gibil initgibil init writes your Hetzner token + defaults to ~/.gibil/config.json. You only run it once.
Quick start
import { run, claudeCode } from "@ai-hero/sandcastle";
import { gibil } from "@gibil/sandcastle-provider";
await run({
agent: claudeCode("claude-opus-4-7"),
sandbox: gibil({ ttl: "30m" }),
promptFile: ".sandcastle/prompt.md",
});That's the entire integration. Run it with npx tsx .sandcastle/main.ts and Sandcastle takes care of the rest:
gibil()provisions a fresh Hetzner VM (30–60 seconds).- Sandcastle uploads your worktree to
/root/workspaceon the VM. - The agent runs inside the VM, streaming output line-by-line.
- Commits land on a branch back on your machine.
- The VM is destroyed.
Options
gibil({
ttl: "2h", // any gibil duration: "30m", "2h", "7d", "1mo", "1y"
serverType: "cpx21", // Hetzner type — defaults to gibil's configured default
location: "fsn1", // Hetzner location
name: "agent-fix-42", // explicit name (default: random)
repo: "https://github.com/you/project", // pre-clone via cloud-init
worktreePath: "/root/workspace", // where Sandcastle uploads the worktree
})Everything else (branchStrategy, hooks, maxIterations, idleTimeoutSeconds, completionSignal) is regular Sandcastle and works as documented.
Isolated providers only support branchStrategy: { type: "merge-to-head" } (default) and { type: "branch" }. The { type: "head" } strategy requires a bind-mount provider — VMs can't share a host filesystem.
Implement-then-review pipeline
Reuse one VM across multiple agent runs with createSandbox:
import { createSandbox, claudeCode } from "@ai-hero/sandcastle";
import { gibil } from "@gibil/sandcastle-provider";
await using sandbox = await createSandbox({
branch: "agent/fix-42",
sandbox: gibil({ ttl: "1h" }),
hooks: {
sandbox: {
onSandboxReady: [{ command: "pnpm install" }],
},
},
});
const impl = await sandbox.run({
agent: claudeCode("claude-opus-4-7"),
promptFile: ".sandcastle/implement.md",
maxIterations: 5,
});
const review = await sandbox.run({
agent: claudeCode("claude-sonnet-4-6"),
prompt: "Review the changes and fix any issues you spot.",
});Both runs hit the same VM. pnpm install runs once, not twice. Commits from both accumulate on agent/fix-42.
How it works
| Sandcastle method | gibil provider does |
|---|---|
create | gibil create --json — provisions VM, parses IP and SSH key path |
exec | ssh root@<ip> bash -lc "<cmd>" with line-buffered stdout streaming |
copyIn (dir) | tar czf - -C <hostDir> . piped into remote tar xzf - -C <sandboxDir> |
copyIn (file) | scp <hostFile> root@<ip>:<sandboxPath> |
copyFileOut | scp root@<ip>:<sandboxPath> <hostFile> |
close | gibil destroy <name> --json |
The provider has no runtime dependencies beyond @ai-hero/sandcastle and Node built-ins. It shells out to system ssh, scp, and tar.
Costs and budgets
A typical 5–10 minute agent run on a cax11 ($0.008/hr) or $0.011/hr) costs well under a cent. Set a tight cpx11 (ttl to cap worst-case cost — gibil({ ttl: "30m" }) means the VM is gone in 30 minutes regardless of what happens to the agent loop.
If you're authenticated (gibil auth login), the provider picks up plan limits and usage tracking automatically.
Caveats
- Cold start: 30–90 seconds. Hetzner provisioning + cloud-init. Use
createSandboxto amortize across multiple runs. - stderr is buffered, not streamed line-by-line. Sandcastle's
onLineis wired to stdout only (matching the Vercel reference provider).stderris returned in full inExecResult.stderr. - No
interactiveExecyet. Usegibil ssh <name>directly if you want a shell into a running VM. - Requires
ssh,scp,taron$PATH. Preinstalled on macOS and most Linux. On Windows, use WSL.
Source and issues
The provider source lives in integrations/sandcastle/ inside the gibil repo. File issues at github.com/AlexikM/gibil/issues.