14 KiB
title, description
| title | description |
|---|---|
| Go SDK | Go client library for the OpenSandbox API covering lifecycle, execd, and egress operations. |
OpenSandbox Go SDK
Go client library for the OpenSandbox API.
Covers all three OpenAPI specs:
- Lifecycle -- Create, manage, and destroy sandbox instances
- Execd -- Execute commands, manage files, monitor metrics inside sandboxes
- Egress -- Inspect and mutate sandbox network policy at runtime
Installation
# go 1.20+
go get github.com/alibaba/OpenSandbox/sdks/sandbox/go
Quick Start
Create and manage a sandbox
package main
import (
"context"
"fmt"
"log"
"github.com/alibaba/OpenSandbox/sdks/sandbox/go"
)
func main() {
ctx := context.Background()
lc := opensandbox.NewLifecycleClient("http://localhost:8080/v1", "your-api-key")
sbx, err := lc.CreateSandbox(ctx, opensandbox.CreateSandboxRequest{
Image: opensandbox.ImageSpec{URI: "python:3.12"},
Entrypoint: []string{"/bin/sh"},
ResourceLimits: opensandbox.ResourceLimits{
"cpu": "500m",
"memory": "512Mi",
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created sandbox: %s (state: %s)\n", sbx.ID, sbx.Status.State)
sbx, err = lc.GetSandbox(ctx, sbx.ID)
if err != nil {
log.Fatal(err)
}
list, err := lc.ListSandboxes(ctx, opensandbox.ListOptions{
States: []opensandbox.SandboxState{opensandbox.StateRunning},
PageSize: 10,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Running sandboxes: %d\n", list.Pagination.TotalItems)
_ = lc.PauseSandbox(ctx, sbx.ID)
_ = lc.ResumeSandbox(ctx, sbx.ID)
_ = lc.DeleteSandbox(ctx, sbx.ID)
}
Run a command with streaming output
exec := opensandbox.NewExecdClient("http://localhost:9090", "your-execd-token")
err := exec.RunCommand(ctx, opensandbox.RunCommandRequest{
Command: "echo 'Hello from sandbox!'",
Timeout: 30000,
}, func(event opensandbox.StreamEvent) error {
switch event.Event {
case "stdout":
fmt.Print(event.Data)
case "stderr":
fmt.Fprintf(os.Stderr, "%s", event.Data)
case "execution_complete":
fmt.Println("\n[done]")
}
return nil
})
Check egress policy
egress := opensandbox.NewEgressClient("http://localhost:18080", "your-egress-token")
policy, err := egress.GetPolicy(ctx)
fmt.Printf("Mode: %s, Default: %s\n", policy.Mode, policy.Policy.DefaultAction)
updated, err := egress.PatchPolicy(ctx, []opensandbox.NetworkRule{
{Action: "allow", Target: "api.example.com"},
})
Use Credential Vault
Credential Vault injects outbound credentials from the egress sidecar while
keeping real secrets out of sandbox environment variables, commands, files, and
logs. Create the sandbox with CredentialProxy enabled, then write credentials
and bindings through the sandbox helpers or EgressClient.
config := opensandbox.ConnectionConfig{
Domain: "localhost:8080",
Protocol: "http",
APIKey: "your-api-key",
}
sandbox, err := opensandbox.CreateSandbox(ctx, config, opensandbox.SandboxCreateOptions{
Image: "python:3.11",
NetworkPolicy: &opensandbox.NetworkPolicy{
DefaultAction: "deny",
Egress: []opensandbox.NetworkRule{
{Action: "allow", Target: "api.example.com"},
},
},
CredentialProxy: &opensandbox.CredentialProxyConfig{Enabled: true},
})
if err != nil {
return err
}
_, err = sandbox.CreateCredentialVault(ctx, opensandbox.CredentialVaultCreateRequest{
Credentials: []opensandbox.Credential{
{
Name: "api-token",
Source: opensandbox.InlineCredentialSource{
Type: opensandbox.CredentialSourceInline,
Value: "<token>",
},
},
},
Bindings: []opensandbox.CredentialBinding{
{
Name: "api-token",
Match: opensandbox.CredentialMatch{
Schemes: []opensandbox.CredentialScheme{opensandbox.CredentialSchemeHTTPS},
Ports: []int{443},
Hosts: []string{"api.example.com"},
Paths: []string{"/v1/*"},
},
Auth: opensandbox.CredentialAuth{
Type: opensandbox.CredentialAuthAPIKey,
Name: "x-api-key",
Credential: "api-token",
},
},
},
})
See Credential Vault for auth types, binding guidance, and Git/curl examples.
Sandbox Pool (Client-Side)
Use SandboxPool to keep an idle buffer of ready sandboxes and reduce acquire latency.
::: warning Experimental
SandboxPool is still evolving based on production feedback and may introduce breaking changes in future releases.
:::
package main
import (
"context"
"fmt"
"log"
"time"
opensandbox "github.com/alibaba/OpenSandbox/sdks/sandbox/go"
)
func main() {
ctx := context.Background()
pool, err := opensandbox.NewSandboxPoolBuilder().
PoolName("demo-pool").
OwnerID("worker-1").
MaxIdle(3).
ConnectionConfig(opensandbox.ConnectionConfig{
Domain: "api.opensandbox.io",
}).
CreationSpec(opensandbox.PoolCreationSpec{
Image: "ubuntu:22.04",
}).
StateStore(opensandbox.NewInMemoryPoolStateStore()). // single-process only
Build()
if err != nil {
log.Fatal(err)
}
if err := pool.Start(ctx); err != nil {
log.Fatal(err)
}
failFast := opensandbox.AcquirePolicyFailFast
sb, err := pool.Acquire(ctx, opensandbox.AcquireOptions{
SandboxTimeout: 10 * time.Minute,
Policy: &failFast,
})
if err != nil {
log.Fatal(err)
}
result, err := sb.RunCommand(ctx, "echo pool-ok", nil)
if err == nil {
fmt.Println(result.Text())
}
_ = sb.Kill(context.Background())
// Drain idle sandboxes before shutdown (single-process cleanup).
pool.ReleaseAllIdle(ctx)
_ = pool.Shutdown(ctx, true)
}
For distributed deployment with multiple processes or pods, use RedisPoolStateStore.
The store accepts a caller-managed redis.Client and does not create or close Redis
connections.
import (
"github.com/redis/go-redis/v9"
opensandbox "github.com/alibaba/OpenSandbox/sdks/sandbox/go"
"github.com/alibaba/OpenSandbox/sdks/sandbox/go/poolredis"
)
redisClient := redis.NewClient(&redis.Options{
Addr: "redis.example.com:6379",
})
store, err := poolredis.NewRedisPoolStateStore(poolredis.RedisPoolStateStoreConfig{
Client: redisClient,
KeyPrefix: "opensandbox:pool:prod",
})
if err != nil {
log.Fatal(err)
}
pool, err := opensandbox.NewSandboxPoolBuilder().
PoolName("prod-pool").
OwnerID("worker-1").
MaxIdle(10).
StateStore(store).
ConnectionConfig(opensandbox.ConnectionConfig{
Domain: "api.opensandbox.io",
}).
CreationSpec(opensandbox.PoolCreationSpec{
Image: "ubuntu:22.04",
}).
PrimaryLockTTL(60 * time.Second).
Build()
::: info Pool Lifecycle Semantics
Acquire()is only allowed when pool state isRUNNING.- In
DRAINING/STOPPED,Acquire()returns*PoolNotRunningError. MaxIdleis the target/cap for ready idle sandboxes. It is not a global limit on borrowed sandboxes or sandboxes created byDirectCreate.OwnerIDis the lock owner identity (node/process id), not the pool identifier. If omitted, SDK auto-generates a default.- Use
WarmupSandboxPreparer(...)if you need to prepare a sandbox after warmup readiness succeeds and before it is put into the idle pool. :::
::: tip Distributed Deployment
InMemoryPoolStateStoreis for single-process development and tests.- For distributed deployment, all nodes in one logical pool must share the same Redis key prefix and
PoolName. - All nodes sharing one pool must use the same creation and warmup definition. If that definition changes, use a new
PoolNameor key prefix and drain the old pool. Resize(ctx, maxIdle)can be called from any node. The call returns after the target is stored in the shared state store; the current primary applies replenish or shrink work during periodic reconcile.- Use
Resize(ctx, 0)and wait forSnapshot().IdleCount == 0to drain a distributed idle buffer.ReleaseAllIdle()is only a best-effort cleanup pass in distributed mode. - Configure
PrimaryLockTTLgreater thanWarmupReadyTimeoutplus expected warmup preparer time. :::
API Reference
LifecycleClient
Created with NewLifecycleClient(baseURL, apiKey string, opts ...Option).
| Method | Description |
|---|---|
CreateSandbox(ctx, req) |
Create a new sandbox from a container image |
GetSandbox(ctx, id) |
Get sandbox details by ID |
ListSandboxes(ctx, opts) |
List sandboxes with filtering and pagination |
DeleteSandbox(ctx, id) |
Delete a sandbox |
PauseSandbox(ctx, id) |
Pause a running sandbox |
ResumeSandbox(ctx, id) |
Resume a paused sandbox |
RenewExpiration(ctx, id, expiresAt) |
Extend sandbox expiration time |
GetEndpoint(ctx, sandboxID, port, useServerProxy) |
Get public endpoint for a sandbox port |
GetSignedEndpoint(ctx, sandboxID, port, expires) |
Get signed endpoint URL with OSEP-0011 route token |
ExecdClient
Created with NewExecdClient(baseURL, accessToken string, opts ...Option).
Health:
| Method | Description |
|---|---|
Ping(ctx) |
Check server health |
Code Execution:
| Method | Description |
|---|---|
ListContexts(ctx, language) |
List active code execution contexts |
CreateContext(ctx, req) |
Create a code execution context |
GetContext(ctx, contextID) |
Get context details |
DeleteContext(ctx, contextID) |
Delete a context |
DeleteContextsByLanguage(ctx, language) |
Delete all contexts for a language |
ExecuteCode(ctx, req, handler) |
Execute code with SSE streaming |
InterruptCode(ctx, sessionID) |
Interrupt running code |
Command Execution:
| Method | Description |
|---|---|
CreateSession(ctx) |
Create a bash session |
RunInSession(ctx, sessionID, req, handler) |
Run command in session with SSE |
DeleteSession(ctx, sessionID) |
Delete a bash session |
RunCommand(ctx, req, handler) |
Run a command with SSE streaming |
InterruptCommand(ctx, sessionID) |
Interrupt running command |
GetCommandStatus(ctx, commandID) |
Get command execution status |
GetCommandLogs(ctx, commandID, cursor) |
Get command stdout/stderr |
File Operations:
| Method | Description |
|---|---|
GetFileInfo(ctx, path) |
Get file metadata |
DeleteFiles(ctx, paths) |
Delete files |
SetPermissions(ctx, req) |
Change file permissions |
MoveFiles(ctx, req) |
Move/rename files |
SearchFiles(ctx, dir, pattern) |
Search files by glob pattern |
ListDirectory(ctx, path) |
List immediate directory contents (server-side default depth) |
ListDirectoryWithDepth(ctx, path, depth) |
List directory contents up to the given depth (0 returns empty) |
ReplaceInFiles(ctx, req) |
Text replacement in files |
UploadFile(ctx, file, opts) |
Upload a file to the sandbox |
UploadFiles(ctx, entries) |
Upload multiple files to the sandbox |
DownloadFile(ctx, remotePath, rangeHeader) |
Download a file from the sandbox |
Directory Operations:
| Method | Description |
|---|---|
CreateDirectory(ctx, path, mode) |
Create a directory (mkdir -p) |
DeleteDirectory(ctx, path) |
Delete a directory recursively |
Metrics:
| Method | Description |
|---|---|
GetMetrics(ctx) |
Get system resource metrics |
WatchMetrics(ctx, handler) |
Stream metrics via SSE |
EgressClient
Created with NewEgressClient(baseURL, authToken string, opts ...Option).
| Method | Description |
|---|---|
GetPolicy(ctx) |
Get current egress policy |
PatchPolicy(ctx, rules) |
Merge rules into current policy |
CreateCredentialVault(ctx, req) |
Create sandbox-local Credential Vault state |
GetCredentialVault(ctx) |
Get sanitized Credential Vault state |
PatchCredentialVault(ctx, req) |
Atomically mutate credentials and bindings |
DeleteCredentialVault(ctx) |
Delete sandbox-local Credential Vault state |
ListCredentialVaultCredentials(ctx) |
List sanitized credential metadata |
GetCredentialVaultCredential(ctx, name) |
Get sanitized metadata for one credential |
ListCredentialVaultBindings(ctx) |
List sanitized binding metadata |
GetCredentialVaultBinding(ctx, name) |
Get sanitized metadata for one binding |
SSE Streaming
Methods that stream output (RunCommand, ExecuteCode, RunInSession, WatchMetrics) accept an EventHandler callback:
type EventHandler func(event StreamEvent) error
Each StreamEvent contains:
Event-- the event type (e.g."stdout","stderr","result","execution_complete"). For NDJSON streams, this is extracted from the JSONtypefield automatically.Data-- the raw event payload (JSON string for NDJSON streams).ID-- optional event identifier
Return a non-nil error from the handler to stop processing the stream early.
Client Options
All client constructors accept optional Option functions:
client := opensandbox.NewLifecycleClient(url, key,
opensandbox.WithHTTPClient(myHTTPClient),
)
client := opensandbox.NewExecdClient(url, token,
opensandbox.WithTimeout(60 * time.Second),
)
::: info TLS Certificate Strength
SDK-created HTTP clients enforce NIST 2030 minimum TLS certificate strength by default (RSA >= 2048, EC >= 224, DSA P >= 2048/Q >= 224, hash >= 224). If you must interoperate with legacy endpoints, set AllowWeakServerCertKeyLengths: true in TransportConfig.
:::
Error Handling
Non-2xx responses are returned as *opensandbox.APIError:
_, err := lc.GetSandbox(ctx, "nonexistent")
if apiErr, ok := err.(*opensandbox.APIError); ok {
fmt.Printf("HTTP %d: %s — %s\n", apiErr.StatusCode, apiErr.Response.Code, apiErr.Response.Message)
}
License
Apache 2.0