Skip to content
Markdown

Go SDK

One Go client for every Runtime Cloud product, with a context.Context on every call.

The module is withruntime.com/go, imported as package withruntime. It needs Go 1.23 or later and uses only the standard library.

Terminalgo get withruntime.com/go

The client finds its key by itself. It uses RUNTIME_API_KEY when that is set, and otherwise the connection this machine saved (any npx withruntime command connects it, with one browser approval).

On a server, put a key from https://withruntime.com/account/keys in RUNTIME_API_KEY from your secret manager. Never put it in source code, a URL or a command-line argument. With no key anywhere, withruntime.New returns an error whose code is missing_api_key and whose hint says how to get one.

Updated

On this page

Hello, sandbox#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, &withruntime.CreateOptions{Funding: "trial"})	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	result, err := sbx.Exec(ctx, "python3 -c 'print(6 * 7)'", &withruntime.ExecOptions{Check: true})	if err != nil {		log.Fatal(err)	}	fmt.Print(result.Stdout)}

Create returns once the sandbox is running. opts may be nil: with no options you get the free trial while it lasts, the default region, and 2 vCPU, 4 GiB of memory and a 4 GiB disk for up to 30 minutes. Funding: "trial" never falls back to paid credit. Every field is optional:

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, &withruntime.CreateOptions{		Name:           "tests-42",		Labels:         map[string]string{"team": "search", "job": "42"},		VCPU:           2,		MemoryMiB:      4096,		DiskMiB:        8192,		TimeoutSeconds: 900,		OnLeaseEnd:     "stop",		Network: &withruntime.Network{			Internet: true,			Allow:    []string{"proxy.golang.org", "*.githubusercontent.com"},		},	})	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	fmt.Println(sbx.ID(), sbx.Info().Funding, sbx.Info().ExpiresAt)}

A field newer than the SDK goes in Extra, which is merged into the request.

Run commands#

Exec runs a string under bash -c. ExecArgv runs a program directly, with no shell, which is what you want for untrusted arguments.

Gopackage mainimport (	"context"	"fmt"	"log"	"os"	"time"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	if _, err := sbx.Exec(ctx, "mkdir -p app && echo 'print(1 + 1)' > app/main.py", nil); err != nil {		log.Fatal(err)	}	run, err := sbx.ExecArgv(ctx, []string{"python3", "main.py"}, &withruntime.ExecOptions{		Cwd:     "/workspace/app",		Env:     map[string]string{"API_TOKEN": os.Getenv("API_TOKEN")},		Timeout: 2 * time.Minute,	})	if err != nil {		log.Fatal(err)	}	if *run.ExitCode != 0 {		fmt.Println(run.Stderr)	}	fmt.Print(run.Stdout)}
  • Env is how secrets reach a command. It is never echoed back, and journals record a hash, not the value. Never put a secret in the command line itself.
  • Stdin gives the command input, then closes it.
  • The default timeout is 60 seconds; the maximum is 24 hours. A timeout is a result (TimedOut is true, with the output so far), not an error.
  • Check: true returns a *withruntime.CommandError, with the exit code and output, on a non-zero exit or a timeout.
  • A result holds at most 64 KiB of Stdout and 64 KiB of Stderr, and StdoutTruncated or StderrTruncated says when more was dropped. With OnStdout or OnStderr (or a Timeout over 60 seconds) the command streams instead, and the result keeps everything it printed.

Stream output as it happens with callbacks, or range over the events:

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	_, err = sbx.Exec(ctx, "for i in 1 2 3; do echo line $i; sleep 1; done", &withruntime.ExecOptions{		OnStdout: func(text string) { fmt.Print(text) },	})	if err != nil {		log.Fatal(err)	}	for event, err := range sbx.ExecStream(ctx, "node --version", nil) {		if err != nil {			log.Fatal(err)		}		switch event.Type {		case "stdout":			fmt.Print(event.Data)		case "exit":			fmt.Println("exit", *event.ExitCode)		}	}}

A long stream resumes by itself when the server ends it, so no output is dropped. The sandbox keeps the latest 1 MiB of a command's output; a reader that falls further behind gets a truncated event.

Background processes#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	server, err := sbx.Spawn(ctx, "python3 -m http.server 8000", &withruntime.SpawnOptions{Cwd: "/workspace"})	if err != nil {		log.Fatal(err)	}	repl, err := sbx.SpawnArgv(ctx, []string{"python3", "-i", "-q"}, &withruntime.SpawnOptions{PipeStdin: true})	if err != nil {		log.Fatal(err)	}	if err := repl.Write(ctx, []byte("print(21 * 2)\n"), true); err != nil {		log.Fatal(err)	}	done, err := repl.Wait(ctx)	if err != nil {		log.Fatal(err)	}	fmt.Print(done.Stdout)	processes, err := sbx.Processes(ctx)	if err != nil {		log.Fatal(err)	}	for _, process := range processes {		fmt.Println(process.ID, process.State, process.Command)	}	if err := server.Kill(ctx, "SIGTERM"); err != nil {		log.Fatal(err)	}}

process.Output(ctx, 0) yields every event from the start until the process exits. A process outlives your connection; get it back with sbx.Process(ctx, id). Write tracks input offsets, so a retried write is never typed twice.

Files#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	if err := sbx.Files.Write(ctx, "/workspace/data/input.csv", []byte("a,b\n1,2\n")); err != nil {		log.Fatal(err)	}	text, err := sbx.Files.ReadText(ctx, "/workspace/data/input.csv")	if err != nil {		log.Fatal(err)	}	fmt.Print(text)	entries, err := sbx.Files.List(ctx, "/workspace", &withruntime.FileListOptions{Depth: 2})	if err != nil {		log.Fatal(err)	}	for _, entry := range entries {		fmt.Println(entry.Type, entry.Size, entry.Path)	}	if err := sbx.Files.Mkdir(ctx, "/workspace/out", true); err != nil {		log.Fatal(err)	}	if err := sbx.Files.Rename(ctx, "/workspace/data/input.csv", "/workspace/out/input.csv", false); err != nil {		log.Fatal(err)	}	if err := sbx.Files.Upload(ctx, "./testdata", "/workspace/project"); err != nil {		log.Fatal(err)	}	if err := sbx.Files.Download(ctx, "/workspace/out", "./out"); err != nil {		log.Fatal(err)	}}

Write makes parent directories and replaces the file atomically; a file over 1 MiB goes in parallel chunks checked by SHA-256. Stat returns nil for a path that does not exist. Upload and Download move a whole directory as one gzipped archive.

Pause, wake, extend, fork and snapshot#

Gopackage mainimport (	"context"	"fmt"	"log"	"time"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, &withruntime.CreateOptions{Funding: "trial"})	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	if err := sbx.Pause(ctx, nil); err != nil { // memory and files are kept; compute billing stops		log.Fatal(err)	}	again, err := client.Sandboxes.Get(ctx, sbx.ID())	if err != nil {		log.Fatal(err)	}	if err := again.Wake(ctx, 20*time.Minute, nil); err != nil {		log.Fatal(err)	}	if err := again.Extend(ctx, 10*time.Minute, nil); err != nil {		log.Fatal(err)	}	copies, err := again.Fork(ctx, &withruntime.ForkOptions{Count: 2, Funding: "trial"})	if err != nil {		log.Fatal(err)	}	for _, copied := range copies {		fmt.Println("fork", copied.ID(), copied.State())		_ = copied.Stop(ctx, nil)	}	snapshot, err := again.Snapshot(ctx, &withruntime.SnapshotOptions{Name: "ready", RetentionDays: 7})	if err != nil {		log.Fatal(err)	}	fromSnapshot, err := client.Sandboxes.Create(ctx, &withruntime.CreateOptions{Snapshot: snapshot.ID, Funding: "trial"})	if err != nil {		log.Fatal(err)	}	_ = fromSnapshot.Stop(ctx, nil)	if err := client.Snapshots.Delete(ctx, snapshot.ID); err != nil {		log.Fatal(err)	}}

A fork copies a sandbox as it is now, with its files, memory and running processes, on the same server; see JavaScript for what forks and snapshots keep and how they are billed. If a copy fails, the error's Details["startedSandboxIds"] names the copies that did start.

Share a port#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	if _, err := sbx.Spawn(ctx, "python3 -m http.server 3000", nil); err != nil {		log.Fatal(err)	}	preview, err := sbx.Previews.Create(ctx, 3000, nil)	if err != nil {		log.Fatal(err)	}	fmt.Println(preview.URL, *preview.Token) // send the token as x-runtime-preview-token}

A preview is private by default; Visibility: "public" shares it with anyone who has the address. Addresses are under runtimehost.com.

Sandboxes by name, and keeping one running#

Sandboxes.GetOrCreate(ctx, name, opts) answers the sandbox with that name, woken if paused and restarted if stopped and persistent, or creates one with opts; Info().Reused says which. KeepAlive extends the lease from your process, so ten minutes remain, until Stop or StopKeepAlive. Persistent: true on a paid sandbox renews the lease on the server instead.

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	dev, err := client.Sandboxes.GetOrCreate(ctx, "dev", &withruntime.CreateOptions{IdlePauseSeconds: 900})	if err != nil {		log.Fatal(err)	}	fmt.Println(dev.ID(), dev.Info().Reused)	dev.KeepAlive(ctx, nil)	defer dev.StopKeepAlive()	off := false	if err := dev.Update(ctx, withruntime.SandboxSettings{AutoWake: &off}, nil); err != nil {		log.Fatal(err)	}}

An interactive terminal#

sbx.Terminal opens a shell over a WebSocket. It is an io.ReadWriteCloser: write what you type, read what it prints.

Gopackage mainimport (	"context"	"io"	"log"	"os"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	term, err := sbx.Terminal(ctx, &withruntime.TerminalOptions{Cols: 120, Rows: 40})	if err != nil {		log.Fatal(err)	}	defer term.Close()	if _, err := term.Write([]byte("uname -a; exit\n")); err != nil {		log.Fatal(err)	}	if _, err := io.Copy(os.Stdout, term); err != nil {		log.Fatal(err)	}}

Images, volumes and snapshots#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	image, err := client.Images.Build(ctx, withruntime.CreateImageOptions{		Name:   "data",		Recipe: &withruntime.ImageRecipe{Pip: []string{"pandas"}, Apt: []string{"jq"}},	}, func(line withruntime.ImageLogLine) { fmt.Println(line.Text) })	if err != nil {		log.Fatal(err)	}	volume, err := client.Volumes.Create(ctx, withruntime.CreateVolumeOptions{SizeMiB: 10240, Name: "cache"})	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, &withruntime.CreateOptions{		Image:   image.ID,		Volumes: []withruntime.VolumeMount{{VolumeID: volume.ID, Path: "/data"}},	})	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	result, err := sbx.Exec(ctx, "python3 -c 'import pandas; print(pandas.__version__)'", nil)	if err != nil {		log.Fatal(err)	}	fmt.Print(result.Stdout)}

Images.Build waits until the image is ready and returns an *Error whose code is image_failed when the build fails; Images.Create queues it and returns. A Dockerfile build takes Dockerfile and ContextDir, the folder docker build would read, with its .dockerignore; only the parts of the folder the server does not have yet are uploaded. Images.Resolve, Tag, Untag, Versions, FollowLogs and Images.Registries.Set for private images do the rest; see custom images. A volume lives on one server. Daily backups are enabled by default; BackedUp becomes true after a backup has been copied off the host and checked. A sandbox with volumes cannot be snapshotted.

Code interpreter, network rules and secrets#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Create(ctx, nil)	if err != nil {		log.Fatal(err)	}	defer sbx.Stop(context.Background(), nil)	if _, err := sbx.Interpreter.Run(ctx, "import math\nx = math.pi", nil); err != nil {		log.Fatal(err)	}	cell, err := sbx.Interpreter.Run(ctx, "round(x * 2, 3)", nil)	if err != nil {		log.Fatal(err)	}	fmt.Println(cell.Results[0].Data["text/plain"])	policy, err := sbx.Network.Set(ctx, withruntime.Network{Internet: true, Allow: []string{"pypi.org", "*.pythonhosted.org"}})	if err != nil {		log.Fatal(err)	}	fmt.Println(policy.Enforced, policy.Allow)	secret, err := client.Secrets.Set(ctx, "OPENAI_API_KEY", withruntime.SetSecretOptions{		Value: "sk-...",		Hosts: []string{"api.openai.com"},	})	if err != nil {		log.Fatal(err)	}	fmt.Println(secret.Placeholder) // what sandboxes see in $OPENAI_API_KEY}

sbx.Desktop drives a Linux desktop in the sandbox (Start, Open, Click, Type, Press, Screenshot), as in JavaScript.

Metrics, events and webhooks#

Gopackage mainimport (	"context"	"fmt"	"io"	"log"	"net/http"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	hook, err := client.Webhooks.Create(ctx, withruntime.CreateWebhookOptions{URL: "https://example.com/hooks/runtime"})	if err != nil {		log.Fatal(err)	}	secret := hook.Secret // shown once; keep it	http.HandleFunc("/hooks/runtime", func(w http.ResponseWriter, r *http.Request) {		body, _ := io.ReadAll(r.Body)		event, err := withruntime.VerifyWebhook(body, r.Header.Get("Runtime-Signature"), []string{secret}, 0)		if err != nil {			http.Error(w, "bad signature", http.StatusUnauthorized)			return		}		fmt.Println(event.Type)		w.WriteHeader(http.StatusNoContent)	})	for event, err := range client.Events.All(ctx, &withruntime.EventListOptions{Type: "sandbox.stopped"}) {		if err != nil {			log.Fatal(err)		}		fmt.Println(event.CreatedAt, event.Type)	}}

sbx.Metrics(ctx, "15m") returns the sandbox's measured CPU and memory, and client.Otel.Create pushes events and metrics to an OpenTelemetry endpoint. See metrics and webhooks.

An endpoint newer than the SDK#

client.Request calls any API path with the client's key, retries, idempotency keys and errors:

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	var page map[string]any	if err := client.Request(context.Background(), "GET", "/v1/volumes", nil, &page); err != nil {		log.Fatal(err)	}	fmt.Println(page["data"])}

Find sandboxes again#

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	filter := &withruntime.ListOptions{Labels: map[string]string{"team": "search"}, State: []string{"running"}}	for sbx, err := range client.Sandboxes.All(ctx, filter) {		if err != nil {			log.Fatal(err)		}		fmt.Println(sbx.ID(), sbx.State())	}}

Sandboxes.List returns one page (Data, HasMore, Next); All walks every page.

Errors and retries#

Gopackage mainimport (	"context"	"errors"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	_, err = client.Sandboxes.Get(ctx, "00000000-0000-4000-8000-000000000000")	var failure *withruntime.Error	switch {	case errors.Is(err, withruntime.ErrNotFound):		fmt.Println("no such sandbox")	case errors.As(err, &failure):		fmt.Println(failure.Code, failure.Hint, failure.RequestID)	}}

Every error is a *withruntime.Error with Code, Message, Hint, RequestID, Status and Details. errors.Is matches it against ErrAuthentication, ErrPermissionDenied, ErrNotFound, ErrConflict, ErrInvalidRequest, ErrRateLimited, ErrServiceUnavailable and ErrConnection, the same classes as the JavaScript and Python SDKs.

Every write carries an idempotency key, made for you and kept across the client's own retries. Transport failures, 429, 502, 503 and 504 are retried with the same key, so a retry never makes two sandboxes or runs a command twice. Retryable() says whether trying the same call again may work.

A create waits for room. When every trial slot is taken (trial_busy), the account is at its limit (quota_exceeded) or the region is full (no_capacity), Sandboxes.Create waits and sends the same request again, for up to two minutes. A burst of CI jobs past the limit queues instead of failing. Set withruntime.WithWaitForCapacity on the client, or WaitForCapacity on one create; zero fails at once.

Read-only keys and daily limits#

An owner, admin or developer can make a read-only key and set a daily spending limit on a key at API keys. client.Limits.Get(ctx) reads both; past the limit, a create, wake or extension returns an error whose code is spending_limit_reached, and it is not retried. See security.

Configuration#

Gopackage mainimport (	"context"	"fmt"	"log"	"os"	"time"	"withruntime.com/go")func main() {	client, err := withruntime.New(		withruntime.WithAPIKey(os.Getenv("MY_RUNTIME_KEY")),		withruntime.WithTimeout(2*time.Minute),		withruntime.WithMaxRetries(4),		withruntime.WithWaitForCapacity(10*time.Minute),	)	if err != nil {		log.Fatal(err)	}	me, err := client.Me(context.Background())	if err != nil {		log.Fatal(err)	}	fmt.Println(me.OrgID)}
  • RUNTIME_API_URL points the client at another API origin, as WithBaseURL does.
  • Code inside a Runtime sandbox calls Runtime's API at http://runtime.internal (Runtime's API from inside a sandbox).
  • The client is safe for concurrent use. Make one and share it: it keeps its connections open and holds at most 32 calls in flight (WithMaxConnections).
  • It honours HTTPS_PROXY and NO_PROXY. WithHTTPClient supplies your own *http.Client.
  • Money is integer microdollars: 1,000,000 is one US dollar. client.Usage(ctx) returns the account's balance as exact decimal strings.

Domains, public ports and private networking#

client.Domains, Ports, Addresses and Tunnel are account products. Domains and public ports name the sandbox they serve. Tunnel manages a WireGuard network; sbx.OpenTunnel and sbx.PortForward open authenticated TCP connections from your process into one sandbox.

When using OpenTunnel directly, read responses while writing requests. Each stream keeps at most 1 MiB of unread response data. If its reader falls behind that bound, reads return tunnel_receive_overflow after the buffered data; other streams keep working. PortForward drains both directions for you.

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Get(ctx, "11111111-2222-4333-8444-555555555555")	if err != nil {		log.Fatal(err)	}	domain, err := client.Domains.Add(ctx, withruntime.AddDomainOptions{		Hostname: "app.example.com", SandboxID: sbx.ID(), Port: 3000,	})	if err != nil {		log.Fatal(err)	}	fmt.Println(domain.Records) // Publish these at your DNS provider, then call Domains.Verify.	address, err := client.Addresses.Reserve(ctx, 4)	if err != nil {		log.Fatal(err)	}	fmt.Println(address.Address, address.Funded, address.FundedUntil)	forward, err := sbx.PortForward(ctx, 5432, "127.0.0.1:0")	if err != nil {		log.Fatal(err)	}	defer forward.Close()	fmt.Println(forward.Addr()) // Connect your local database client here.}

Ports.Open returns Connect, the public address and port. Tunnel.Create creates the account's WireGuard network; AddPeer and RotatePeer return a configuration holding a generated private key once. Save Config even when ConfigReady is false: it is a draft whose gateway public key must be filled from Tunnel.Get before use. A generated-key request is never automatically retried. To keep the private key entirely on your machine, supply PublicKey.

Dedicated addresses and WireGuard networks expose Funded, FundedUntil, RateMicros and RateUnit. Exhausted credit disables traffic while retaining the reservation until you release it. SSO.Get reads sign-on and directory status and the website address where an owner manages it.

Backup and restore volumes#

Volumes.Backup requests a checked copy off the host. Backups lists copies; GetBackup and DeleteBackup read or remove one. SetBackupPolicy changes daily backups or retention. Restore makes a new volume from a ready backup.

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	backup, err := client.Volumes.Backup(ctx, "11111111-2222-4333-8444-555555555555", nil)	if err != nil {		log.Fatal(err)	}	if backup.State != "ready" {		fmt.Println("Backup is still running; poll Volumes.GetBackup:", backup.ID)		return	}	volume, err := client.Volumes.Restore(ctx, backup.ID, &withruntime.CreateVolumeOptions{Name: "restored"})	if err != nil {		log.Fatal(err)	}	fmt.Println(volume.ID, volume.State, volume.RestoredFrom)}

Permissions and file watching#

Pass FileWriteOptions to write permissions with the content; a pointer keeps mode 000 distinct from the default. Current guest images require only write_file. Older images require exec for the SDK's chmod fallback. Files.Chmod sets an existing file's permissions through the same exec grant. A supplied write key identifies the whole upload: each mutation phase has its own stable key, and a repeated completed upload returns without sending its chunks again.

Gopackage mainimport (	"context"	"fmt"	"log"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Get(ctx, "11111111-2222-4333-8444-555555555555")	if err != nil {		log.Fatal(err)	}	mode := uint32(0o755)	if err := sbx.Files.Write(ctx, "/workspace/run.sh", []byte("#!/bin/sh\necho ready\n"), &withruntime.FileWriteOptions{Mode: &mode}); err != nil {		log.Fatal(err)	}	watch, err := sbx.Files.Watch(ctx, "/workspace", &withruntime.WatchOptions{Recursive: true, Exclude: []string{"node_modules"}})	if err != nil {		log.Fatal(err)	}	defer watch.Stop(context.Background())	for batch, err := range watch.Events(ctx) {		if err != nil {			log.Fatal(err)		}		fmt.Println(batch.Kind, batch.Events, batch.Reason, watch.Cursor())	}}

Events reconnects on a continuation and preserves its cursor. A pause ends iteration without stopping the guest watch; after waking the sandbox, call Events again. overflow and lost notices mean you should rescan. Breaking iteration closes its HTTP response. Stop also stops the guest watch. Files.Watches.List and Read support finding watches and polling instead.

Bucket mounts, MCP servers and recordings#

sbx.Mounts.Add mounts a customer bucket using the name of a Runtime secret, with List and Remove for lifecycle management. The sandbox never receives the bucket key. client.MCP.Catalog lists available servers; sbx.MCP.Start, Ready, Get and Stop manage servers running in that sandbox. sbx.Desktop.Recordings has Start, Get, List, Stop, Download and Delete; downloaded bytes are MP4.

Gopackage mainimport (	"context"	"fmt"	"log"	"time"	"withruntime.com/go")func main() {	ctx := context.Background()	client, err := withruntime.New()	if err != nil {		log.Fatal(err)	}	sbx, err := client.Sandboxes.Get(ctx, "11111111-2222-4333-8444-555555555555")	if err != nil {		log.Fatal(err)	}	catalog, err := client.MCP.Catalog(ctx)	if err != nil {		log.Fatal(err)	}	fmt.Println(catalog)	_, err = sbx.MCP.Start(ctx, withruntime.MCPStartOptions{Servers: []withruntime.MCPServerRequest{{ID: "github", Secrets: map[string]string{"GITHUB_PERSONAL_ACCESS_TOKEN": "GITHUB_TOKEN"}}}})	if err != nil {		log.Fatal(err)	}	gateway, err := sbx.MCP.Ready(ctx, 5*time.Minute)	if err != nil {		log.Fatal(err)	}	fmt.Println(gateway.Servers, gateway.Warnings) // Connect with gateway.Headers; keep its token private.}

Try it on your own work. Sign up, then paste the setup prompt into your coding agent.

Claim 100 hours free

Was this page right?