This document describes how the CLI resolves a gateway and communicates with it once the endpoint already exists. The gateway exposes gRPC and HTTP services on a single multiplexed port, and the CLI chooses one of three connection modes: direct mTLS, edge-authenticated WebSocket tunnel, or plaintext HTTP/2 behind a trusted proxy.
When any CLI command needs to talk to the gateway, it resolves the target through a priority chain (crates/openshell-cli/src/main.rs -- resolve_gateway()):
--gateway-endpoint <URL>flag (direct URL, reusing stored metadata when the gateway is known).--cluster <NAME>/-g <NAME>flag.OPENSHELL_GATEWAYenvironment variable.- Active gateway from
~/.config/openshell/active_gateway.
Resolution loads ClusterMetadata from disk to get the gateway_endpoint URL and auth_mode. When --gateway-endpoint is used, the CLI still tries to match the URL to stored metadata so edge auth tokens and TLS bundles continue to resolve by cluster name.
graph TD
CLI["CLI Command"]
RESOLVE["Resolve Gateway"]
MODE{"auth_mode?"}
MTLS["mTLS Channel"]
EDGE["Edge Tunnel"]
PLAIN["Plaintext Channel"]
GW["Gateway (gRPC + HTTP)"]
CLI --> RESOLVE
RESOLVE --> MODE
MODE -->|"null / mtls"| MTLS
MODE -->|"cloudflare_jwt"| EDGE
MODE -->|"plaintext endpoint"| PLAIN
MTLS -->|"TLS + client cert"| GW
EDGE -->|"WSS tunnel + JWT"| GW
PLAIN -->|"HTTP/2 plaintext"| GW
File: crates/openshell-cli/src/tls.rs -- build_channel()
The default mode for self-deployed gateways. The CLI loads three PEM files from ~/.config/openshell/clusters/<name>/mtls/:
| File | Purpose |
|---|---|
ca.crt |
Cluster CA certificate -- verifies the gateway's server cert |
tls.crt |
Client certificate -- proves the CLI's identity to the gateway |
tls.key |
Client private key |
These are used to build a tonic::transport::ClientTlsConfig, which configures a tonic::transport::Channel for gRPC communication over HTTP/2 with mTLS.
sequenceDiagram
participant CLI as CLI
participant GW as Gateway
CLI->>CLI: Load ca.crt, tls.crt, tls.key
CLI->>GW: TCP connect to gateway_endpoint
CLI->>GW: TLS handshake (present client cert)
GW->>GW: Verify client cert against CA
GW-->>CLI: TLS established (HTTP/2 via ALPN)
CLI->>GW: gRPC requests (OpenShell / Inference service)
Files: crates/openshell-cli/src/edge_tunnel.rs, crates/openshell-cli/src/auth.rs
For gateways behind an edge proxy (e.g., Cloudflare Access), the CLI routes traffic through a local WebSocket tunnel proxy:
start_tunnel_proxy()binds an ephemeral local TCP port.- Opens a WebSocket connection (
wss://<gateway>/_ws_tunnel) to the edge with the stored bearer token in headers. - The gateway's
ws_tunnel.rshandler upgrades the WebSocket and bridges it to an in-memoryMultiplexServiceinstance. - The gRPC channel connects to
http://127.0.0.1:<local_port>(plaintext HTTP/2 over the tunnel).
Authentication uses a browser-based flow: gateway add opens the user's browser to the gateway's /auth/connect endpoint, which reads the CF_Authorization cookie and relays it back to a localhost callback server. The token is stored at ~/.config/openshell/clusters/<name>/edge_token.
When the gateway is deployed with --plaintext, TLS is disabled entirely. The CLI connects over plain HTTP/2. This mode is intended for gateways behind a trusted reverse proxy or tunnel that handles TLS termination.
All connection artifacts are stored under $XDG_CONFIG_HOME/openshell/ (default ~/.config/openshell/):
openshell/
active_cluster # plain text: active cluster name
clusters/
<name>_metadata.json # ClusterMetadata JSON
<name>/
mtls/ # mTLS bundle (when TLS enabled)
ca.crt # cluster CA certificate
tls.crt # client certificate
tls.key # client private key
edge_token # Edge auth JWT (when auth_mode=cloudflare_jwt)
For gateways that are already deployed behind an edge proxy (e.g., Cloudflare Access), deployment is not needed -- only registration.
File: crates/openshell-cli/src/run.rs -- gateway_add()
sequenceDiagram
participant U as User
participant CLI as openshell-cli
participant Browser as Browser
participant Edge as Edge Proxy
participant GW as Gateway
U->>CLI: openshell gateway add https://gw.example.com
CLI->>CLI: Store metadata (auth_mode: cloudflare_jwt)
CLI->>Browser: Open https://gw.example.com/auth/connect
Browser->>Edge: Edge proxy login
Edge-->>Browser: CF_Authorization cookie
Browser->>GW: GET /auth/connect (with cookie)
GW-->>Browser: Relay page (extracts token, POSTs to localhost)
Browser->>CLI: POST token to localhost callback
CLI->>CLI: Store edge_token
CLI->>CLI: save_active_cluster
CLI-->>U: Gateway added and set as active