Files
Jim Meyer 8c7dd148a9 perf(net): set TCP_NODELAY on latency-sensitive TCP hops (#2220)
* perf(net): set TCP_NODELAY on all tunnel and proxy TCP hops

The sandbox tunnel added ~44 ms of latency to every small
request/response because no socket in the path disabled Nagle's
algorithm, so sub-MSS writes waited on delayed ACKs at each hop.

Set TCP_NODELAY on every latency-sensitive TCP socket:

- gateway: accepted connections on the public listener (gRPC relay
  frames and WS tunnel writes)
- CLI: edge tunnel local accept + underlying WebSocket TCP stream,
  insecure TLS connector (tonic's default connector already does
  this), and service-forward accepted sockets
- supervisor: direct-tcpip connect into the sandbox netns, TCP relay
  target dials, egress proxy accepted connections, and all upstream
  CONNECT/HTTP dials (via a new connect_upstream helper)

Setting TCP_NODELAY on connect is best-effort: a failure only costs
latency, so we log and continue rather than fail the connection.

The sandbox SSH transport rides a unix domain socket and gRPC client
channels use tonic defaults (nodelay on), so no change is needed there.

Fixes #2219

Signed-off-by: Jim Meyer <jim@meyer4hire.com>

* refactor(net): house TCP_NODELAY helper in a shared net module

Address review feedback on the TCP_NODELAY change:

- Move the shared set-nodelay helper out of supervisor_session into a
  new crate-private `net` module in openshell-supervisor-process, so
  ssh and supervisor_session no longer reach across modules through a
  pub(crate) item.
- Make the best-effort comments at each call site terse and consistent.

No behavior change; the benchmark ladder reproduces the same numbers.

Signed-off-by: Jim Meyer <jim@meyer4hire.com>

* refactor(net): consolidate TCP_NODELAY helpers into openshell_core::net

Move the best-effort TCP_NODELAY helpers into the shared openshell_core::net
module so every crate dials and configures sockets the same way:

- Add set_tcp_nodelay_best_effort (accepted/existing streams) and
  connect_tcp_nodelay_best_effort (dial + set) with unit tests.
- Migrate all call sites in openshell-cli, openshell-server, and the
  supervisor crates to the shared helpers.
- Remove the crate-private net module from openshell-supervisor-process.
- Document socket guidance in AGENTS.md (Network Sockets).

Signed-off-by: Jim Meyer <jim@meyer4hire.com>

* perf(net): set TCP_NODELAY on exec bridge and metadata server

The gateway-side single-use SSH-over-relay loopback bridge and the
sandbox IMDS metadata server were missed latency-sensitive TCP hops.
Set TCP_NODELAY on the accepted client connection and both russh client
dials of the exec bridge — interactive keystrokes and line-buffered PTY
output are the most tinygram-heavy traffic in the system — and on the
metadata server's accepted connections.

Also log unrecognized MaybeTlsStream variants in the edge tunnel so a
future TLS-backend change surfaces a silent TCP_NODELAY miss instead of
skipping it quietly.

Signed-off-by: Jim Meyer <jim@meyer4hire.com>

* perf(net): set TCP_NODELAY on openshell-sdk socket paths

The openshell-sdk crate landed on main with its own copies of the CLI's
hand-rolled sockets, which the CLI and TUI are meant to consume. Give
them the same treatment as the CLI equivalents:

- edge_tunnel: the accepted local tunnel connection and the WebSocket's
  underlying TCP socket (plain and rustls variants).
- transport: the dial in InsecureTlsConnector, tonic's custom-connector
  path.

Only these hand-rolled sockets need it. Tonic's own connector defaults
tcp_nodelay to true and applies it itself, so plain Endpoint::connect
callers were already covered.

Signed-off-by: Jim Meyer <jim@meyer4hire.com>

---------

Signed-off-by: Jim Meyer <jim@meyer4hire.com>
2026-08-04 17:59:13 +00:00
..

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, 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).

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/raw_inference accessors do not refresh; they return a client bound to the current token. When a refresher is wired, use raw_grpc_fresh/raw_inference_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, 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. Failures map to a typed SdkError with a discriminable kind.

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.

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.