Files
OpenShell/sdk/go/docs/src/architecture.md
Roland HussandDrew Newberry 8dc55e21ee feat(sdk/go): complete Go SDK with domain clients, auth, and hardening (#2702)
* feat(sdk/go): complete Go SDK with domain clients, auth, and hardening

Add the Go SDK implementation covering all gateway RPC services with
domain-typed clients, comprehensive OIDC authentication flows, fake
test doubles, and proto converters.

Domain clients: Sandbox, Provider, Exec, File, TCP, SSH, Policy,
Profile, Health, Service, Config, Workspace, Inference, Refresh.
Each client validates inputs, resolves sandboxes by name, and converts
between domain types and proto at the boundary.

Auth: OIDC authorization code (PKCE), device code (RFC 8628), and
client credentials (RFC 6749 Section 4.4) flows with gateway config
auto-resolution. Token refresh with singleflight deduplication and
exponential backoff. Edge tunnel proxy for gRPC-over-WebSocket.

Gateway: On-disk gateway discovery with user/system directory
precedence, lazy token loading, and auth mode mapping.

Testing: In-memory fake client with deep-copy isolation, watch
broadcasting with filtering, and workspace-scoped object stores.
Bufconn-based gRPC tests for all domain clients.

Ref: #2044

Signed-off-by: Roland Huß <rhuss@redhat.com>

* feat(sdk/go): add gateway client options for logger, timeout, and retry

Add WithLogger, WithTimeout, and WithRetryPolicy options to the gateway
package's NewClient function. These forward the corresponding Config
fields (Logger, Timeout, RetryPolicy) through to the underlying SDK
client, giving callers full control over observability, connection
timeouts, and retry behavior when constructing clients from on-disk
gateway configurations.

Also adds RetryPolicy type to types package and extends Config with the
three new fields.

* fix(mise): sync sccache lock entry

Signed-off-by: Drew Newberry <anewberry@nvidia.com>

---------

Signed-off-by: Roland Huß <rhuss@redhat.com>
Signed-off-by: Drew Newberry <anewberry@nvidia.com>
Co-authored-by: Drew Newberry <anewberry@nvidia.com>
2026-08-13 19:15:21 +00:00

4.2 KiB

Architecture

This page explains how the OpenShell Go SDK is structured internally. Understanding the design helps you navigate the API surface and write idiomatic code.

Client Hierarchy

The SDK follows the Kubernetes client-go sub-client pattern. A single Client provides typed accessors for each API domain:

Client
├── Sandboxes()   → SandboxInterface
├── Exec()        → ExecInterface
├── Providers()   → ProviderInterface
│   ├── Profiles()  → ProfileInterface
│   └── Refresh()   → RefreshInterface
├── Services()    → ServiceInterface
├── Files()       → FileInterface
├── Health()      → HealthInterface
├── SSH()         → SSHInterface
├── TCP()         → TCPInterface
├── Config()      → ConfigInterface
└── Policy()      → PolicyInterface

Each accessor returns an interface. You work with the interface, not the concrete implementation. This makes the sub-clients easy to mock and test.

Creating a Client

All interaction starts with NewClient:

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()

The Config struct controls:

Field Purpose
Address Gateway host and port
Auth Authentication provider (StaticToken, NoAuth, or custom)
TLS TLS settings (CA cert, skip verify, client certs)
Retry Retry policy for transient failures

Proto Isolation

The SDK never exposes protobuf-generated types in its public API. Instead, it defines its own Go types (in openshell/v1/) and converts to/from proto at the gRPC boundary.

This means:

  • Your code imports openshell/v1, not proto/openshellv1
  • You work with plain Go structs, not proto messages
  • Proto schema changes in upstream OpenShell do not break your code (the SDK adapts internally)
  • You can use standard Go patterns (json.Marshal, fmt.Sprintf, reflect) on SDK types without proto constraints

The conversion layer lives in openshell/v1/internal/converter/ and is not part of the public API.

Sub-Client Pattern

Each sub-client groups methods for a specific API domain. For example, SandboxInterface provides Create, Get, List, Delete, WaitReady, Watch, and more.

Sub-clients are cheap to access. They are created once when the Client is initialized and reuse the same underlying gRPC connection:

// These return the same sub-client instance every time
sandboxes := client.Sandboxes()
exec := client.Exec()

Some sub-clients have their own sub-clients. ProviderInterface exposes Profiles() and Refresh():

profiles, err := client.Providers().Profiles().List(ctx, "default")
status, err := client.Providers().Refresh().GetStatus(ctx, "default", "openai", "api-key")

gRPC Layer

Underneath, the SDK communicates with the OpenShell gateway over gRPC. The single OpenShell service in proto/openshell.proto defines all RPCs. The SDK maps each interface method to one or more RPCs:

Pattern Example
Unary RPC Create, Get, Delete
Server-streaming RPC Watch, Stream, GetLogs
Client-streaming RPC File uploads
Bidirectional streaming Interactive, Forward

The gRPC connection is managed by the Client. Calling client.Close() cleanly shuts down all active streams and the underlying connection.

Error Model

All SDK methods return standard Go errors. Errors from the gateway carry a StatusError with a typed error code. Use the Is* functions to classify errors:

_, err := client.Sandboxes().Get(ctx, "default", "missing")
if v1.IsNotFound(err) {
    // sandbox does not exist
}

See the Error Handling guide for the complete list of error checks and retry patterns.

Fake Client

For testing, the SDK provides openshell/v1/fake with an in-memory implementation of ClientInterface. The fake client supports fixture seeding, watch events, and health simulation:

fc := fake.NewClient()
fc.AddSandbox(&v1.Sandbox{Name: "test-sb", Status: v1.SandboxStatus{Phase: v1.SandboxReady}})

See the Testing guide for complete examples.