OpenShell SDK for Go

[!IMPORTANT]
Read the full documentation for guides, API reference with gRPC mapping, and testing patterns.
A Go SDK for interacting with OpenShell
servers, providing idiomatic Go bindings for shell session management, command
execution, provider configuration, and service exposure.
Why a Go SDK?
Go is the language of the Kubernetes ecosystem. If you want to build an
operator, controller, or any automation that manages OpenShell resources as
native Kubernetes objects, you need a Go client.
This SDK is modeled after
k8s.io/client-go, the standard
Kubernetes client library that every Go operator developer already knows. The
patterns will look familiar:
- Typed sub-clients per resource:
client.Sandboxes(), client.Providers(),
client.Exec(), just like clientset.CoreV1().Pods()
- Domain types separated from wire formats: clean Go structs in a
types
package, no proto leakage into the public API (like k8s.io/api)
- Watch primitives: channel-based watchers with
ResultChan() and Stop(),
identical to watch.Interface in client-go
- Functional options: variadic option patterns for list filtering,
pagination, and watch configuration
- Composable auth with token refresh: wraps
oauth2.TokenSource for
automatic token caching and coalesced refresh, following the k8s client-go
cachingTokenSource pattern
- Fake client for testing: an in-memory implementation of the full client
interface (like
k8s.io/client-go/kubernetes/fake), so operators can be tested
without a real gateway
Quick Start
import v1 "github.com/rhuss/openshell-sdk-go/openshell/v1"
// Connect to a gateway
client, err := v1.NewClient(v1.Config{
Address: "gateway.example.com:443",
Auth: v1.StaticToken("my-token"),
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Create a sandbox and wait until it's ready
sandbox, err := client.Sandboxes().Create(ctx, "my-sandbox", &v1.SandboxSpec{
Template: &v1.SandboxTemplate{Image: "python:3.12"},
}, nil)
if err != nil {
log.Fatal(err)
}
sandbox, err = client.Sandboxes().WaitReady(ctx, sandbox.Name)
if err != nil {
log.Fatal(err)
}
// Run a command
result, err := client.Exec().Run(ctx, sandbox.Name,
[]string{"python3", "-c", "print('hello from sandbox')"},
v1.ExecOptions{},
)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(result.Stdout))
With automatic token refresh
For OIDC gateways, use RefreshableToken to wrap any oauth2.TokenSource with
automatic caching and coalesced refresh:
import "golang.org/x/oauth2"
tokenSource := oauth2Config.TokenSource(ctx, initialToken)
auth, err := v1.RefreshableToken(tokenSource,
v1.WithLeeway(30*time.Second),
)
if err != nil {
log.Fatal(err)
}
client, err := v1.NewClient(v1.Config{
Address: "gateway.example.com:443",
Auth: auth,
})
if err != nil {
log.Fatal(err)
}
defer client.Close()
Concurrent callers share a single refresh call. If the token source fails, the
SDK falls back to the cached token with a logged warning. See the
Auth docs for details.
When a gateway sits behind a zero-trust reverse proxy, use WithExtraHeaders to
attach proxy-specific headers alongside standard auth:
base := v1.StaticToken("my-gateway-token")
auth, err := v1.WithExtraHeaders(base, map[string]string{
"x-proxy-auth": "proxy-secret",
})
if err != nil {
log.Fatal(err)
}
client, err := v1.NewClient(v1.Config{
Address: "gateway.example.com:443",
Auth: auth,
})
For Cloudflare Access, use the convenience constructor in the edge package:
import "github.com/rhuss/openshell-sdk-go/openshell/v1/edge"
auth, err := edge.CloudflareAccess(base, os.Getenv("CF_ACCESS_TOKEN"))
For gRPC behind edge proxies that reject HTTP/2, use the WebSocket tunnel:
tunnel, err := edge.NewTunnelProxy(
"wss://gateway.example.com/ws",
os.Getenv("CF_ACCESS_TOKEN"),
)
if err != nil {
log.Fatal(err)
}
defer tunnel.Close()
client, err := v1.NewClient(v1.Config{
Address: tunnel.Addr(),
Auth: v1.StaticToken("my-token"),
TLS: &v1.TLSConfig{Insecure: true}, // local tunnel, no TLS
})
OIDC Login
The oidc package provides gateway-aware OIDC authentication with browser,
keyboard, device code, and client credentials flows:
import "github.com/rhuss/openshell-sdk-go/openshell/v1/oidc"
// Gateway-aware login: reads OIDC config from gateway metadata
token, err := oidc.Login(ctx, "my-gateway")
if err != nil {
log.Fatal(err)
}
// Use the token with the SDK client
client, err := v1.NewClient(v1.Config{
Address: "gateway.example.com:443",
Auth: v1.StaticToken(token.AccessToken),
})
For headless environments, use the device code flow:
token, err := oidc.DeviceLogin(ctx,
oidc.WithIssuer("https://auth.example.com"),
oidc.WithClientID("my-app"),
)
For service accounts, use client credentials:
token, err := oidc.ClientCredentials(ctx,
oidc.WithGateway("my-gateway"),
oidc.WithClientSecret("service-secret"),
)
See the oidc package docs for all options and flows.
See the Getting Started guide for the full walkthrough.
Inference Route Management
Configure how inference requests are routed for a workspace:
// Set an inference route
route, err := client.Inference().SetRoute(ctx, "my-workspace", &v1.InferenceRouteConfig{
ProviderName: "openai",
ModelID: "gpt-4",
RouteName: "", // empty string = default route
TimeoutSecs: 120,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Route v%d: %s/%s\n", route.Version, route.ProviderName, route.ModelID)
// Retrieve the route
route, err = client.Inference().GetRoute(ctx, "my-workspace", "")
if err != nil {
log.Fatal(err)
}
// Delete the route
err = client.Inference().DeleteRoute(ctx, "my-workspace", "")
if err != nil {
log.Fatal(err)
}
Architecture
Client
├── Sandboxes() → SandboxInterface (create, get, list, delete, watch, wait, logs)
├── Exec() → ExecInterface (run, stream, interactive)
├── Files() → FileInterface (upload, download)
├── Health() → HealthInterface (health check, gateway info, current user)
├── Services() → ServiceInterface (expose, get, list, delete)
├── Providers() → ProviderInterface (CRUD + ensure)
│ ├── Profiles() → ProfileInterface (list, get, import, update, lint, delete)
│ └── Refresh() → RefreshInterface (configure, status, rotate, delete)
├── Workspaces() → WorkspaceInterface (create, get, list, delete, members)
├── Inference() → InferenceInterface (set, get, delete inference routes)
└── Policy() → PolicyInterface (draft review, approve, reject, merge, status)
All domain types live in openshell/v1/types/. Proto-to-Go conversions happen in
an internal converter layer. The public API surface uses type aliases so
consumers import a single package. See the Architecture overview for details.
Features
| Feature |
Interface |
Docs |
| Sandbox lifecycle (create, get, list, delete, watch, wait) |
SandboxInterface |
Sandboxes |
| Command execution (collected, streamed, interactive PTY) |
ExecInterface |
Exec |
| Provider management (CRUD + idempotent ensure) |
ProviderInterface |
Providers |
| Provider profiles (list, import, lint, update) |
ProfileInterface |
Profiles |
| Credential refresh (configure, rotate, status) |
RefreshInterface |
Refresh |
| Service exposure (expose, list, delete) |
ServiceInterface |
Services |
| File transfer (upload, download) |
FileInterface |
Files |
| Policy management (draft review, approve, reject, merge) |
PolicyInterface |
Policy |
| Sandbox logs (streaming retrieval) |
SandboxInterface |
Sandboxes |
| Workspace management (create, get, list, delete, members) |
WorkspaceInterface |
Workspaces |
| Inference route management (set, get, delete) |
InferenceInterface |
Inference |
| Gateway info and current user identity |
HealthInterface |
Health |
| Health checking |
HealthInterface |
Health |
| SSH tunneling and TCP forwarding |
SSHInterface, TCPInterface |
SSH, TCP |
| Auth: static token, refreshable token (oauth2.TokenSource) |
AuthProvider |
Auth |
| Edge auth: extra headers, Cloudflare Access, WebSocket tunnel |
AuthProvider, edge.TunnelProxy |
Edge |
Typed errors (IsNotFound, IsAlreadyExists, IsConflict, ...) |
StatusError |
Error Handling |
| Real-time watch with auto-stop on terminal phase |
WatchInterface[T] |
Sandboxes |
| Fake client for testing (no gRPC server needed) |
fake.Client |
Testing |
| OIDC login (browser, keyboard, device code, client credentials) |
oidc.Login, oidc.DeviceLogin, oidc.ClientCredentials |
OIDC |
| Gateway config convenience (load CLI gateway configs, auto-wire auth) |
gateway.NewClient, gateway.LoadConfig |
Gateway |
Prerequisites
- Go 1.23 or later
- mise (recommended for reproducible builds)
Build and Test
git clone https://github.com/rhuss/openshell-sdk-go.git
cd openshell-sdk-go
make test # Run tests with coverage
make lint # Run golangci-lint
make ci # Full CI pipeline (lint + build + test)
If you don't have mise installed, make will print installation instructions.
Documentation
Full API documentation is available at the OpenShell Go SDK Docs site.
To build the docs locally:
cargo install mdbook
mdbook serve docs
Contributing
See CONTRIBUTING.md for development setup, build commands,
and contribution guidelines.
License
Apache-2.0. See LICENSE for details.
Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.