- Update CI (unit-test.yml, pr-test.yml) to use yaoapp/tai:1.2.0 with new default ports (gRPC:19100, HTTP:8099, VNC:16080, Docker:12375) - Add explicit 0.0.0.0 bind for containerized Tai instances - Fix sandbox/v2 grpc.go default port fallback (9100 → 19100) - Fix tai/tunnel/proxy.go fallback ports (8080→8099, 6080→16080) - Sync tai SDK and sandbox/v2 documentation with implementation - Add new docs: api.md, registry.md, tunnel.md Made-with: Cursor
8.1 KiB
Package sandbox
Container lifecycle management. Provides a unified Sandbox interface with three implementations:
| Implementation | Constructor | Backend | Mode |
|---|---|---|---|
| Local | NewLocal(addr) |
Direct Docker daemon | Local |
| Docker | NewDocker(addr) |
Docker via Tai proxy | Remote |
| K8s | NewK8s(addr, opts) |
Kubernetes via Tai proxy | Remote |
Interface
type Sandbox interface {
Create(ctx context.Context, opts CreateOptions) (string, error)
Start(ctx context.Context, id string) error
Stop(ctx context.Context, id string, timeout time.Duration) error
Remove(ctx context.Context, id string, force bool) error
Exec(ctx context.Context, id string, cmd []string, opts ExecOptions) (*ExecResult, error)
ExecStream(ctx context.Context, id string, cmd []string, opts ExecOptions) (*StreamHandle, error)
Inspect(ctx context.Context, id string) (*ContainerInfo, error)
List(ctx context.Context, opts ListOptions) ([]ContainerInfo, error)
Close() error
}
StreamHandle
type StreamHandle struct {
Stdin io.WriteCloser
Stdout io.Reader
Stderr io.Reader
Wait func() (int, error) // blocks until exec finishes, returns exit code
Cancel func() // aborts the exec process
}
ExecStream provides real-time I/O access to a running exec process. Unlike Exec which collects all output, ExecStream returns immediately with readers/writers for interactive use.
Constructors
NewLocal
func NewLocal(addr string) (Sandbox, error)
Connects directly to a Docker daemon. addr can be:
""— platform default (Unix socket on Linux/macOS, named pipe on Windows)"unix:///var/run/docker.sock"— explicit Unix socket"tcp://host:port"— explicit TCP
Pings the daemon on creation; returns an error if unreachable.
NewDocker
func NewDocker(addr string) (Sandbox, error)
Connects to Docker Engine API through Tai's Docker proxy. addr should be "tcp://tai-host:12375".
NewK8s
func NewK8s(addr string, opts ...K8sOption) (Sandbox, error)
Connects to Kubernetes through Tai's TCP proxy. Each sandbox maps to a single-container Pod.
Parameters:
addr—"host:port"pointing to Tai's K8s proxy endpointopts.KubeConfig— path to kubeconfig file (required). Relative paths are resolved to absolute.opts.Namespace— Kubernetes namespace (default"default")
The constructor overrides the kubeconfig's server field to point at addr, enables insecure TLS (since Tai does TCP passthrough), and verifies connectivity by querying the namespace.
All pods created by K8s sandbox are labeled with managed-by: yao-tai-sdk.
Types
CreateOptions
type CreateOptions struct {
Name string // container/pod name
Image string // container image
Cmd []string // entrypoint command
Env map[string]string // environment variables
Binds []string // volume binds (Docker only)
WorkingDir string // working directory
Memory int64 // memory limit in bytes, 0 = no limit
CPUs float64 // CPU limit, 0 = no limit
VNC bool // enable VNC port mapping (Local and Docker modes)
Ports []PortMapping // port mappings (Docker only)
Labels map[string]string // container/pod labels for discovery and management
User string // container user, e.g. "1000:1000" or "sandbox"
}
PortMapping
type PortMapping struct {
ContainerPort int // port inside the container
HostPort int // port on the host, 0 = random
HostIP string // host bind address, default "127.0.0.1"
Protocol string // "tcp" (default) or "udp"
}
ContainerInfo
type ContainerInfo struct {
ID string // container/pod ID
Name string // container/pod name
Image string // image name
Status string // "created", "running", "exited", "removing" (Docker)
// "Pending", "Running", "Succeeded", "Failed" (K8s)
IP string // container/pod IP address
Ports []PortMapping // mapped ports (Docker only)
Labels map[string]string // container/pod labels
}
ExecOptions
type ExecOptions struct {
WorkDir string // override working directory
Env map[string]string // additional environment variables
}
ExecResult
type ExecResult struct {
ExitCode int
Stdout string
Stderr string
}
ListOptions
type ListOptions struct {
All bool // include stopped containers
Labels map[string]string // filter by labels
}
K8sOption
type K8sOption struct {
Namespace string // default "default"
KubeConfig string // path to kubeconfig file (required)
}
Behavioral Differences
| Behavior | Docker (Local/Remote) | K8s |
|---|---|---|
Create returns |
container ID (hash) | pod name |
Start |
starts a stopped container | polls until pod leaves Pending (up to 60s) |
Stop |
stops with timeout, container persists | deletes the pod with grace period |
Remove(force=true) |
force-removes | deletes with grace period 0 |
Exec |
Docker exec API | kubectl exec via SPDY |
Inspect.Ports |
populated from Docker | always empty |
List |
filters only by opts.Labels (no auto label) |
auto-merges managed-by=yao-tai-sdk + opts.Labels |
Binds |
supported | not supported |
VNC flag |
auto port-maps 6080 and 5900 (all platforms) | not applicable |
Image Interface
type Image interface {
Exists(ctx context.Context, ref string) (bool, error)
Pull(ctx context.Context, ref string, opts PullOptions) (<-chan PullProgress, error)
Remove(ctx context.Context, ref string, force bool) error
List(ctx context.Context) ([]ImageInfo, error)
}
Accessed via c.Image() on the top-level client. Nil when the Tai server has no container runtime.
| Implementation | Constructor | Backend | Notes |
|---|---|---|---|
| Docker | NewDockerImage(cli) |
Docker SDK | Shared by Local and Docker-via-Tai modes |
| K8s | NewK8sImage() |
No-op | Image pulling is handled by kubelet |
DockerCli Helper
func DockerCli(sb Sandbox) *client.Client
Extracts the underlying Docker SDK client from a Sandbox (Local or Docker). Returns nil for K8s sandboxes. Used internally to construct NewDockerImage(DockerCli(sb)).
Types
type PullOptions struct {
Auth *RegistryAuth // nil = anonymous / public
}
type RegistryAuth struct {
Username string
Password string
Server string // e.g. "ghcr.io", "registry.example.com"
}
type PullProgress struct {
Status string // "Pulling fs layer", "Downloading", "Extracting", "Pull complete", etc.
Layer string // layer digest / short ID
Current int64 // bytes completed
Total int64 // bytes total (0 if unknown)
Error string // non-empty on failure
}
type ImageInfo struct {
ID string
Tags []string
Size int64
Created time.Time
}
Image Example
c, _ := tai.New("tai://192.168.1.100")
defer c.Close()
progress, _ := c.Image().Pull(ctx, "alpine:latest", sandbox.PullOptions{})
for p := range progress {
fmt.Printf("%s %s %d/%d\n", p.Status, p.Layer, p.Current, p.Total)
}
images, _ := c.Image().List(ctx)
for _, img := range images {
fmt.Printf("%s %v\n", img.ID[:12], img.Tags)
}
Sandbox Example
sb, _ := sandbox.NewLocal("")
defer sb.Close()
id, _ := sb.Create(ctx, sandbox.CreateOptions{
Name: "worker",
Image: "alpine:latest",
Cmd: []string{"sleep", "300"},
Env: map[string]string{"FOO": "bar"},
Memory: 256 * 1024 * 1024, // 256 MB
})
sb.Start(ctx, id)
result, _ := sb.Exec(ctx, id, []string{"echo", "$FOO"}, sandbox.ExecOptions{})
fmt.Println(result.Stdout)
sb.Stop(ctx, id, 10*time.Second)
sb.Remove(ctx, id, false)