Gateway discovery
The OpenShell CLI keeps a registration for every gateway it knows: the endpoint, the authentication mode, and the credentials. The OpenShell.Gateway namespace reads the same files with the same rules as the CLI, so an application can connect to whatever gateway the user selected, without its own configuration.
Connect
GatewayClient.Connect loads a registration, builds the authentication and TLS settings from it, and returns an OpenShellClient. Pass no name for the active gateway.
// The gateway the OpenShell CLI made active, with its stored credentials.
using var client = GatewayClient.Connect();
// Or a registered gateway by name.
using var staging = GatewayClient.Connect("staging");
var info = await client.Health.GetGatewayInfoAsync();
Console.WriteLine(info);
GatewayConnectOptions adjusts the result: the default workspace and timeout, your own Auth or Tls in place of the registered ones, whether expired OIDC tokens are renewed (AutoRefresh) and written back (WriteBack), and how a Cloudflare Access gateway is reached (EdgeTunnel).
using var client = GatewayClient.Connect("prod", new GatewayConnectOptions
{
DefaultWorkspace = "team-a",
DefaultTimeout = TimeSpan.FromSeconds(30),
WriteBack = false, // keep renewed tokens in memory; leave oidc_token.json untouched
});
By authentication mode (auth_mode in metadata.json), the client gets:
auth_mode |
GatewayAuthMode | Authentication |
|---|---|---|
missing or none |
None |
No credentials. |
plaintext |
Plaintext |
No credentials, plaintext HTTP. |
mtls |
Mtls |
The client certificate in mtls/tls.crt and mtls/tls.key; fails when they are missing. |
oidc |
Oidc |
The access token in oidc_token.json, renewed with its refresh token and written back. |
cloudflare_jwt |
CloudflareJwt |
The Access token in edge_token (or the legacy cf_token), through a local edge tunnel. |
For an https endpoint, the mtls directory also supplies the trust: the CA in mtls/ca.crt when present, otherwise the system roots, and the client certificate when present.
Where registrations live
| Location | Default | Holds |
|---|---|---|
| User configuration directory | $XDG_CONFIG_HOME/openshell; otherwise %APPDATA%\openshell on Windows and ~/.config/openshell elsewhere |
active_gateway, and gateways/<name>/ with metadata.json and the credentials |
| System configuration directory | $OPENSHELL_SYSTEM_GATEWAY_DIR when it is an absolute path; otherwise /etc/openshell on Linux and macOS, and none on Windows |
active_gateway, and gateways/<name>/metadata.json |
The rules, as in the CLI:
- Active gateway.
$OPENSHELL_GATEWAY, then the useractive_gatewayfile, then the systemactive_gatewayfile. A missing, empty, or invalid file falls through to the next source. - Registrations. A user entry shadows a system registration of the same name only when it has its own
metadata.json. Onlyactive_gatewayandmetadata.jsonfall back to the system directory. - Credentials. The
mtlsbundle,oidc_token.json, andedge_tokenare always read from and written to the user gateway directory, GatewayMetadata.StateDirectory, also for a system registration. GatewayMetadata.Directory is wheremetadata.jsonwas found. - Names. ASCII letters, digits,
-,_, and., so host-derived names such asgw.example.comand192.0.2.1work..and.., a leading or trailing dot, and Windows device names such asCONorCOM1.example.comare rejected on every platform.
Inspect registrations
GatewayLocator exposes the same lookups:
var locator = new GatewayLocator();
Console.WriteLine($"user config: {locator.UserConfigDirectory}");
Console.WriteLine($"system config: {locator.SystemConfigDirectory ?? "(none)"}");
foreach (var entry in locator.List())
{
Console.WriteLine($"{(entry.Active ? "*" : " ")} {entry.Name} ({entry.Source})");
}
var gateway = locator.Load(locator.ResolveActiveName());
Console.WriteLine($"{gateway.Name}: {gateway.Endpoint}, auth {gateway.AuthMode}");
Console.WriteLine($"registration in {gateway.Directory}, credentials in {gateway.StateDirectory}");
List() sorts by name and skips registrations whose metadata cannot be read; Load(name) throws OpenShellException with NotFound or InvalidConfig when it cannot load one.
The token file
oidc_token.json uses the CLI format, so one openshell gateway login serves the CLI and the SDK:
{
"access_token": "<access token>",
"refresh_token": "<refresh token>",
"expires_at": 1793000000,
"issuer": "https://idp.example.com/realms/openshell",
"client_id": "openshell-cli"
}
expires_at is in Unix seconds. A token issued for another issuer or client id than the registration names is never sent. To sign in from .NET, see Signing in to a registered gateway.
Build the options yourself
GatewayClient.CreateOptions returns the OpenShellClientOptions that Connect would use, for settings GatewayConnectOptions does not cover. It does not start an edge tunnel.
// Build the options yourself to set what GatewayConnectOptions does not expose.
// CreateOptions does not start an edge tunnel: use Connect for cloudflare_jwt gateways.
var gateway = new GatewayLocator().Load("prod");
var options = GatewayClient.CreateOptions(gateway) with
{
Retry = RetryPolicy.Default with { MaxAttempts = 6 },
};
using var client = new OpenShellClient(options);
Other directories
GatewayLocatorOptions points the locator at other directories, and replaces the environment lookup, for example in tests:
// Read registrations from other directories, for example in tests.
var locator = new GatewayLocator(new GatewayLocatorOptions
{
UserConfigDirectory = Path.Combine(root, "user"),
SystemConfigDirectory = Path.Combine(root, "system"),
GetEnvironmentVariable = _ => null, // ignore OPENSHELL_GATEWAY and the other variables
});
using var client = GatewayClient.Connect("local", new GatewayConnectOptions { Locator = locator });