# openshell-sdk `openshell-sdk` is the shared async Rust client for OpenShell gateways. It owns gRPC channel setup, TLS, OIDC refresh, and the Cloudflare Access tunnel so the CLI, the TUI, and language bindings share one client implementation. Callers pass an explicit bearer token; the SDK does no filesystem access and no gateway-name resolution. ## Two layers - `OpenShellClient` — the curated, sandbox-focused surface: health, sandbox CRUD, reusable sandbox template CRUD, readiness/deletion waits, and non-streaming exec. - `raw` — direct access to the generated tonic clients for RPCs the curated surface doesn't yet cover (providers, policy, logs, settings, SSH, forwarding). ## Auth and refresh The curated surface drives OIDC refresh automatically: proactively before a request and reactively on `Unauthenticated`. Refreshes are single-flight, so only one is in flight at a time. The plain `raw_grpc` accessor does not refresh; it returns a client bound to the current token. When a refresher is wired, use `raw_grpc_fresh` to refresh before the call, and `force_refresh` to recover after a raw RPC returns `Unauthenticated`. The SDK consumes a `Refresh` trait that the caller implements; it does not run the OIDC browser flow itself. Its non-interactive refresh-token exchange also accepts scopes that identity providers may require to select the API resource for the refreshed access token. ## Transport modes - Plaintext (local development) - Server-authenticated TLS (system roots, or a pinned private CA via `ca_cert`) - OIDC bearer over HTTPS (gateways behind an OAuth2/OIDC IdP) - Cloudflare Access tunnel (hosted gateways) - Insecure TLS (development/debug; certificate verification disabled) mTLS (client certificates) is not supported. ## Public surface `OpenShellClient::connect(ClientConfig)` returns a connected client exposing `health`, `create_sandbox`, `get_sandbox`, `list_sandboxes`, `list_all_sandboxes`, `delete_sandbox`, `create_sandbox_from_template`, `create_sandbox_template`, `get_sandbox_template`, `list_sandbox_templates`, `delete_sandbox_template`, `list_sandboxes_all_workspaces`, `list_sandbox_templates_all_workspaces`, `wait_ready`, `wait_deleted`, and `exec`. Curated types (`SandboxSpec`, `SandboxRef`, `Health`, `ListOptions`, `SandboxTemplateListOptions`, `ExecOptions`, `SandboxPhase`) use SDK-shaped enums rather than raw proto integers where practical. Reusable template resources are exposed as `SandboxWorkloadTemplate` proto aliases so callers can populate the full portable workload shape and driver config. Failures map to a typed `SdkError` with a discriminable kind. Curated calls without a workspace argument explicitly select the `default` workspace. Cross-workspace listing uses the separate `*_all_workspaces` methods and requires Platform Admin access. Curated `list_*` methods return a lazy `Pager`. Each `next_page()` call issues at most one RPC and returns a `Page` with its opaque continuation token. The explicit `list_all_*` conveniences exhaust that pager; `page_size` always controls one gateway request, and `page_token` resumes a saved traversal. ```rust let mut pages = client.list_sandboxes(ListOptions { page_size: 100, ..Default::default() }); while let Some(page) = pages.next_page().await? { for sandbox in page.items { println!("{}", sandbox.name); } } ``` ```rust use openshell_sdk::{ ClientConfig, OpenShellClient, SandboxTemplateCreateSpec, SandboxWorkloadConfig, SandboxWorkloadTemplate, SandboxWorkloadTemplateSpec, }; # async fn run() -> Result<(), openshell_sdk::SdkError> { let client = OpenShellClient::connect(ClientConfig::new("http://127.0.0.1:8080")).await?; client .create_sandbox_template(SandboxWorkloadTemplate { metadata: Some(openshell_sdk::raw::proto::datamodel::v1::ObjectMeta { name: "python".to_string(), ..Default::default() }), spec: Some(SandboxWorkloadTemplateSpec { workload: Some(SandboxWorkloadConfig { image: "ghcr.io/nvidia/openshell-community/sandboxes/python:latest".to_string(), ..Default::default() }), ..Default::default() }), }) .await?; let _sandbox = client .create_sandbox_from_template(SandboxTemplateCreateSpec { template_name: "python".to_string(), policy: Some(openshell_sdk::raw::proto::SandboxPolicy { version: 1, ..Default::default() }), ..Default::default() }) .await?; # Ok(()) # } ``` ## Modules | Module | Purpose | |---|---| | `client` | High-level `OpenShellClient` and the curated sandbox surface. | | `config` | `ClientConfig`, `AuthConfig`. | | `transport` | Channel construction, TLS resolution, request interceptors. | | `auth` | `EdgeAuthInterceptor` for bearer-token attachment. | | `oidc` | OIDC token handling at the transport layer. | | `refresh` | `Refresh` trait and single-flight refresh coalescing. | | `edge_tunnel` | Cloudflare Access tunnel dialer. | | `error` | `SdkError` taxonomy. | | `pagination` | Lazy `Pager` and response `Page`. | | `types` | Curated request/response types and proto conversions. | | `raw` | Escape hatch re-exporting the generated tonic clients. | ## Notes - Async-only. Tonic is async-native; callers needing a blocking call can wrap with their own runtime. - The curated surface will grow as more RPCs graduate from `raw`.