Phase 2b of RFC 0008: settle and implement the Refresh trait contract and connect TokenSource to live request authentication. - TokenSource single-flight now coalesces on a generation counter and shares one in-flight outcome (success or failure) across waiters, so a failing IdP is hit once per attempt rather than once per waiter. - refresh_now() forces a refresh regardless of expiry (recovers a revoked but not-yet-expired token); current() stays expiry-gated. This fixes the documented forced-refresh path that previously no-opped on unexpired tokens. - EdgeAuthInterceptor holds the OIDC bearer in a shared, mutable slot the interceptor reads per request; OpenShellClient refreshes proactively before unary calls and reactively on Unauthenticated (one retry), writing the new token into the slot so rotation reaches an already-connected client. - AuthConfig::Oidc carries optional expires_at + Arc<dyn Refresh> (RFC shape). The CLI keeps a static token (refresh: None), preserving phase-1 behavior. Tests: refresh unit tests for forced-refresh-of-unexpired and shared-failure coalescing; mock-gateway tests for reactive recovery from Unauthenticated and single-attempt-without-refresher. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com>
openshell-sdk
openshell-sdk is the shared async Rust client for OpenShell gateways. It owns
the gRPC transport and auth stack so the CLI, the TUI, and language bindings
share one implementation of channel setup, TLS, OIDC refresh, and the
Cloudflare Access tunnel.
Designed in RFC 0008.
Two layers
OpenShellClient— the curated, sandbox-focused surface: health, sandbox 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 (inference, providers, policy, logs, settings, SSH, forwarding).
Responsibilities
- Construct the gRPC channel and select the transport (plaintext vs TLS).
- Load TLS material and set up mTLS channels.
- Attach edge-auth bearer tokens and refresh OIDC tokens, with single-flight coalescing so only one refresh is in flight at a time.
- Proxy connections through the Cloudflare Access tunnel for hosted gateways.
- Map transport and gateway failures to a typed
SdkErrorwith a discriminable kind.
Non-responsibilities
- Gateway-name resolution, default config-path lookups, and the OIDC browser
flow. These are user-facing concerns owned by
openshell-cli; the SDK consumes aRefreshtrait the CLI implements. - Reading tokens from disk. Callers pass an explicit token; the SDK performs no filesystem access.
- Defining the gRPC contract. The protos and generated types are owned by
openshell-core.
Transport and auth modes
Covers the connectivity modes the CLI exercises in production, except mTLS:
- Plaintext (local development)
- Server-authenticated TLS over HTTPS (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 intentionally out of scope; gateways that require
it continue to use openshell-cli's legacy mTLS path until that auth method is
retired.
Public surface
OpenShellClient::connect(ClientConfig) returns a connected client exposing
health, create_sandbox, get_sandbox, list_sandboxes, delete_sandbox,
wait_ready, wait_deleted, and exec. Curated types (SandboxSpec,
SandboxRef, Health, ListOptions, ExecOptions, SandboxPhase) use
SDK-shaped enums rather than raw proto integers.
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. |
types |
Curated request/response types and proto conversions. |
raw |
Escape hatch re-exporting the generated tonic clients. |
Consumers
openshell-cli, openshell-tui, and openshell-sdk-node (published as
@openshell/sdk).
Notes
- Async-only. Tonic is async-native; callers needing a blocking call can wrap with their own runtime.
- The surface is alpha and will grow as more RPCs graduate from
rawinto the curated client.