Runtime gives goose a disposable Linux machine, and it removes the keyring problem at the same time. goose's Docker guide notes that its keyring settings do not work on Ubuntu in a container. The guide has you pass the API key as an environment variable instead. On Runtime, that variable holds only a placeholder. The key is a Runtime secret, and the host's proxy adds the real value to requests to the provider. Every sandbox is a Firecracker microVM with its own kernel and Ubuntu 24.04. While goose waits on the model, 2 vCPUs and 4 GiB cost $0.03125 an hour. The newest stable tag on 25 October 2026 was v1.56.0.
| Direction | goose runs | Runtime is |
|---|---|---|
| Headless task on a repo | In a sandbox | The machine, driven by the SDK |
| goose session on your laptop | On your machine | A stdio extension with sandbox tools |
Store the provider key
goose reads provider settings from the environment: GOOSE_PROVIDER,
GOOSE_MODEL, and the provider's own key variable. For Anthropic, that variable
is ANTHROPIC_API_KEY. Only the key is sensitive, so only the key is stored as
a secret:
Terminalprintf %s "$ANTHROPIC_API_KEY" | npx withruntime secrets set ANTHROPIC_API_KEY --host api.anthropic.comEach sandbox in your account now starts with ANTHROPIC_API_KEY set to a
placeholder. GOOSE_DISABLE_KEYRING tells goose not to look for a system
keyring. The proxy adds the real key only to HTTPS requests to
api.anthropic.com (secrets sandboxes never see).
Any other provider that goose supports works the same way, with its own
variable and host.
A headless run from your code
TypeScriptimport { writeFile } from "node:fs/promises";import { Sandbox } from "withruntime";const repo = "https://github.com/your-org/your-repo";const task = "Upgrade the project to the current major version of its test runner and fix what breaks.";const installGoose = "curl -fsSL https://github.com/aaif-goose/goose/releases/download/stable/download_cli.sh | CONFIGURE=false bash";await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 3600 });const once = { check: true, timeoutMs: 600_000 } as const;await sbx.exec("sudo apt-get update && sudo apt-get install -y bzip2", once);await sbx.exec(installGoose, once);await sbx.exec(["git", "clone", "--depth", "1", repo, "/workspace/app"], once);// goose is installed; from here it may reach the model API and nothing else.await sbx.network.set({ internet: true, allow: ["api.anthropic.com"] });const run = await sbx.exec( [ "goose", "run", "--no-session", "--with-builtin", "developer", "--output-format", "json", "--max-turns", "60", "-t", task, ], { cwd: "/workspace/app", env: { GOOSE_PROVIDER: "anthropic", GOOSE_MODEL: "claude-sonnet-4-5-20250929", GOOSE_MODE: "auto", GOOSE_DISABLE_KEYRING: "1", GOOSE_DISABLE_SESSION_NAMING: "true", }, timeoutMs: 1_800_000, onStderr: (text) => process.stderr.write(text), },);console.log(run.exitCode, JSON.parse(run.stdout));await sbx.exec("git add -A && git diff --cached > /workspace/goose.patch", { cwd: "/workspace/app", check: true,});await writeFile("goose.patch", await sbx.files.readText("/workspace/goose.patch"));Pythonimport sys, jsonfrom withruntime import Sandboxrepo = "https://github.com/your-org/your-repo"task = "Upgrade the project to the current major version of its test runner and fix what breaks."install_goose = ("curl -fsSL https://github.com/aaif-goose/goose/releases/download/" "stable/download_cli.sh | CONFIGURE=false bash")with Sandbox.create(disk_mib=8192, timeout_seconds=3600) as sbx: sbx.exec("sudo apt-get update && sudo apt-get install -y bzip2", check=True, timeout_ms=600_000) sbx.exec(install_goose, check=True, timeout_ms=600_000) sbx.exec(["git", "clone", "--depth", "1", repo, "/workspace/app"], check=True, timeout_ms=600_000) sbx.network.set(internet=True, allow=["api.anthropic.com"]) run = sbx.exec( ["goose", "run", "--no-session", "--with-builtin", "developer", "--output-format", "json", "--max-turns", "60", "-t", task], cwd="/workspace/app", env={ "GOOSE_PROVIDER": "anthropic", "GOOSE_MODEL": "claude-sonnet-4-5-20250929", "GOOSE_MODE": "auto", "GOOSE_DISABLE_KEYRING": "1", "GOOSE_DISABLE_SESSION_NAMING": "true", }, timeout_ms=1_800_000, on_stderr=sys.stderr.write, ) print(run.exit_code, json.loads(run.stdout)) sbx.exec("git add -A && git diff --cached > /workspace/goose.patch", cwd="/workspace/app", check=True) with open("goose.patch", "w") as file: file.write(sbx.files.read_text("/workspace/goose.patch"))Step by step:
bzip2is installed first because the Linux release is a.tar.bz2file. goose's install guide suggests installing it when extraction fails.CONFIGURE=falseskips the interactivegoose configurestep. The script installs to$HOME/.local/bin. In a sandbox, that is/workspace/.local/bin, which is already first onPATH.- The allow list is one host. goose ships as a single binary, so once it is installed, it needs no package registry. Add the registries that your project's tests install from.
--with-builtin developerloads goose's built-in developer extension, which gives it shell and file editing. With no config file, no other extension loads.GOOSE_MODE=autolets goose run tools without asking.GOOSE_DISABLE_SESSION_NAMINGsaves the extra model call that goose otherwise makes to name the session.--no-sessionwrites no session file.--max-turnscaps the turns taken without user input. The default is 1000.--output-format jsonreturns the result once the run completes. Usestream-jsonto watch events as they happen.
goose run options worth knowing
From goose's CLI command reference, 25 October 2026:
| Option | Purpose |
|---|---|
-t, --text <TEXT> |
The instruction |
-i, --instructions <FILE> |
Instructions from a file |
--recipe <FILE> |
A recipe with a prompt field, for repeatable jobs |
--provider, --model |
Override GOOSE_PROVIDER and GOOSE_MODEL |
-q, --quiet |
Print only the model's response |
--output-format json, stream-json |
Structured output after or during the run |
--max-tool-repetitions <n> |
Stops identical tool calls repeating in a loop |
A recipe is goose's way to repeat a job across many repos. One YAML file holds
the prompt, instructions and extensions. Each sandbox runs goose run --recipe.
To keep a checkout between recipes, pause the sandbox
instead of stopping it.
Add Runtime as a goose extension
goose calls MCP servers extensions. To give a goose session on your laptop Runtime's tools for one run:
Terminalgoose session --with-extension "runtime:npx -y withruntime mcp"The runtime: prefix names the extension. Its tools therefore appear as
runtime__runtime_sandbox_create and so on, not under npx. To keep the
extension, add a stdio extension to ~/.config/goose/config.yaml:
YAMLextensions: runtime: type: stdio name: runtime enabled: true cmd: npx args: ["-y", "withruntime", "mcp"] envs: {} env_keys: [] timeout: 300The first Runtime call prints a link and a code. Approve Connect agent in
the browser. Every tool then loads, and the YAML holds no key
(MCP). goose also accepts remote servers as
streamable_http extensions. https://api.withruntime.com/mcp works with that type.
| Tool | What goose gets |
|---|---|
runtime_sandboxes_create |
A fresh microVM, ready when the call returns |
runtime_sandboxes_exec |
Commands run away from your laptop |
runtime_sandboxes_desktop_screenshot |
A look at a desktop running in the sandbox |
runtime_secrets |
Keys for the sandbox to use without seeing |
Cost
Runtime charges for the CPU a sandbox actually uses, at $0.025 per vCPU-hour, with a floor of a twentieth of a vCPU. Reserved memory costs $0.0075 per GiB-hour. A 2 vCPU, 4 GiB goose run costs $0.03125 an hour while the model works, and $0.08 an hour at full CPU (pricing). The model provider bills tokens on your key. A new account gets 100 free hours of a 1 GB machine in its first 30 days, without a card:
Terminalnpx withruntime sandboxes run -- uname -aOther open agents on Runtime: OpenCode and OpenHands. For many repos at the same time, see a sandbox for coding agents.
Sources
Checked 25 October 2026.
- Install goose:
download_cli.sh,CONFIGURE=false, thebzip2tip - download_cli.sh:
installs to
$HOME/.local/bin - Headless goose:
goose run,--no-session,GOOSE_MODE=auto,GOOSE_DISABLE_SESSION_NAMING - goose in Docker: keys by environment because the keyring does not work on Ubuntu in Docker
- CLI commands:
runoptions,--with-extensionnaming - Environment variables:
GOOSE_PROVIDER,GOOSE_MODEL,GOOSE_DISABLE_KEYRING - Config files:
~/.config/goose/config.yamland extension types - Releases: tag v1.56.0
Facts on this page were checked on 25 October 2026.