Skip to content

Client commands

Commands a user runs on their own machine, Linux or Windows. They read the client configuration ([endpoints], [client], [oidc]) and the identity-token environment variables. Global flags are in the CLI hub.

~/.gdsgate below is %USERPROFILE%\.gdsgate on Windows (%HOME%\.gdsgate when HOME is set).

Access certificates are cached under ~/.gdsgate/<cluster>/<user>/ and reused while in scope and valid. After a CA rotation or a revocation, the cached certificate is rejected at session open: the client says the cached access certificate was rejected, asks for gdsgate login, and clears the cache. The next login or retry issues a new one.

gdsgate connect <host[:port]>

Fetches a cluster's settings and writes the client configuration from them. Run it first against a new cluster; it needs no configuration.

gdsgate connect gdsgate.example.com:50061
PROXY_FINGERPRINT="<approved-proxy-fingerprint>"
gdsgate connect gdsgate.example.com --fingerprint "$PROXY_FINGERPRINT"  # set from the approved bundle

The port defaults to 50061. For enrolled Proxy transport TLS, an IP-only entry uses proxy.gdsgate.internal for the anonymous probe. CA verification uses --sni, the advertised TLS name, or the Proxy role name when neither is set. The verified name is saved in [client].transport_sni. Certificates trusted by the host are checked against the dialled address or --sni.

What it writes:

Path Contents
~/.gdsgate/config.toml [endpoints].proxy_public, [client] (anchor path, server name), the complete public [oidc] profile, the addressing defaults the cluster publishes, and [proxy].public_origin if it has one
~/.gdsgate/transport-ca.pem the confirmed cluster CA, when fingerprint trust is used

Both are owner-only and written atomically. connect selects the configuration it writes. Later commands use that selection unless --config names another file; change it with contexts use.

Without --fingerprint, connect first verifies the gateway certificate against this host's trust store, using the dialled name or --sni. If this succeeds, it needs no prompt and writes a configuration that uses system trust. It does not save a CA file on that path.

For an unknown CA, connect shows the offered fingerprint and asks for confirmation, or compares it with --fingerprint. Without a fingerprint or terminal confirmation, it stops. After confirmation it fetches the settings again over TLS verified against that CA, then writes the verified settings.

An explicit fingerprint requires a matching advertised CA. It is checked even when this host trusts the public certificate. A missing or changed anchor, invalid certificate, unsupported bundle format, or verified RPC refusal stops the operation.

For [proxy].public_cert_file, the bundle publishes the CA in that listener's accepted certificate chain when present. A private CA follows the same confirmation or fingerprint path. With no explicit SNI, the certificate must cover the entry IP or domain. A failed certificate/key replacement keeps the previous certificate and advertised CA; accepting a new CA requires a new trust decision. A leaf-only file relies on existing system trust or an explicit CA.

The operator publishes the fingerprint with gdsgate ca fingerprint:

gdsgate --config /etc/gdsgate/proxy.toml ca fingerprint

Flags:

Flag Effect
--fingerprint <SHA256> Expected transport CA fingerprint. Separators and case ignored. Missing or mismatched CA is an error. Required off a terminal for an unknown CA.
--output <PATH>, -o Write elsewhere. The anchor goes in the same directory.
--force Replace files that already exist. Without it, an existing file is an error and nothing is written.
--sni <NAME> Server name to send and verify, overriding what the cluster advertises. For a front that routes by name.
--insecure Bootstrap from a gateway that serves no TLS. Nothing is encrypted or verified; for development gateways on this host only. Conflicts with --fingerprint.
--timeout <SECS> How long to wait for the gateway. Default 15.

Keep several clusters

Use one configuration directory per cluster. The saved CA filename is always transport-ca.pem in the output directory; different TOML filenames alone do not separate trust anchors. Save and select a configuration for the new cluster:

gdsgate connect production.example.com \
    --output ~/.gdsgate/clusters/production/config.toml
gdsgate login
gdsgate up

Unknown CA trust needs terminal confirmation or the approved --fingerprint. Find saved and discovered configurations with gdsgate contexts list; * marks the current selection. To return to a saved cluster, use contexts use with its path, then login. A running resident is reloaded. Use up when no resident is running. Do not use connect --force to bypass a shared-anchor conflict.

connect does not sign in and leaves the login and certificate cache unchanged; sign in afterwards with gdsgate login, or gdsgate identity bootstrap for a workload. On failure (fingerprint mismatch, declined confirmation, unreachable gateway, invalid value) nothing is written.

Published values are validated as hand-written ones are: a name zone is a single label, address pools must parse, and a browser origin is a bare scheme://host[:port], HTTPS unless on this host. An invalid value is refused by name. An advertised OIDC provider must have a valid HTTPS issuer and a nonempty client ID; an incomplete provider stops the write.

gdsgate login [--browser]

Authenticate to the cluster.

Select a configuration with connect or contexts use, or pass --config. An absent configuration stops login before contacting a gateway.

  • With an [oidc] provider configured, login runs the device flow by default: prints a URL and a short code, waits for approval, then caches the obtained validated identity and optional refresh token in the cluster's identity.json, mode 0600. Later commands reuse or renew it.
  • --browser runs Authorization Code with PKCE over a loopback redirect, so the browser opens on the same machine.
  • Without [oidc], login confirms reachability and a local-dev principal is used (GDSGATE_USER, default dev).

Headless and CI: skip login and supply the token through GDSGATE_ID_TOKEN.

gdsgate up signs in the same way when it finds no identity.

Incomplete [oidc] settings and provider or identity-file failures stop the operation. Correct the named setting or provider error and retry. Login does not change to a development identity on failure.

Follow OIDC sign-in for provider prerequisites, refreshing a saved profile, the MSIS9612 device rule, claim mapping and error diagnosis.

gdsgate up [--browser] [--foreground]

Starts the resident client: publishes a local address for every resource in your catalog, keeps the generated ssh / kubectl / environment configuration in step with it, and holds the identity both need.

Signs in first when there is no identity, as login does; --browser uses the browser flow. Without a terminal (a service, a pipe) it stops and names the command to run.

On Linux with systemd, up installs and starts a system service running as the original user with network capabilities for the default local owner protection. With local_user_only = false and a loopback level, a user service is available. An explicit access = transparent still needs the system service. Elevation is requested once when needed; sudo gdsgate up does not request it again. Without systemd it starts a detached process; Linux owner protection still requires a privileged launcher. On Windows it ensures the network helper is running, while the resident stays in the user's session.

up returns after the resident answers with catalog status. A repeated up reports an existing resident; sudo or an explicit transparent request can upgrade a resident running at a lower level. A failed start exits non-zero and names the service journal, or prints the detached process log. gdsgate down stops it; service uninstall client removes its service.

contexts use PATH reloads a running resident before saving the selection. Passing --config to an ordinary CLI command affects that command only. Use reload to change the resident explicitly. See configuration selection.

--foreground runs the client in this process and holds the terminal. An interrupt stops it with the same teardown as down, and so does losing the terminal: on Linux the terminal closing or its host killed, on Windows the console closing, logoff or shutdown. A multiplexer's detached window does not count. On Linux the shipped systemd unit runs --foreground. Use it to watch a failing start. On a terminal it also appends its output to the journal, without colour.

gdsgate reload

Without --role, reload targets the resident. For server components, see server reload.

Re-reads the file currently loaded by the resident. The process, control socket and network interface remain running. To load another file without changing the CLI selection:

gdsgate reload
gdsgate --config ~/.gdsgate/clusters/test/config.toml reload
gdsgate status --json

Use contexts use PATH to change both the selection and running resident. The managed Linux client unit also supports systemctl reload. A newly registered service starts with its installed configuration. Later contexts use PATH selections survive service restarts and host reboots.

Change Reload behaviour
Gateway endpoints, transport CA/name, OIDC settings Rebuilds cluster clients and identity refresh; subsequent connections use the new settings
Catalog interval, notifications, access warming, retention Applies to the running resident
Direct privileged-port offset Rebinds local listeners and regenerates addresses and commands
Cluster switch Withdraws previous routes and names; identical resource IDs have separate address assignments
Addressing level, pools, basic port range, interface, resolver, zone, local isolation, interception or agent consent Refused before changing live routes; these bound OS resources require a restart

Already admitted sessions retain their original cluster connection. New connections to withdrawn addresses fail. The target cluster uses its own login record; when none exists, run gdsgate login. Delegated and environment-token residents refuse cross-cluster switching.

Unknown keys, invalid values and an unreadable cache refuse reload. status and its JSON report include config_path, cluster, reload_generation and reload_error. A refused reload leaves the current configuration in force. Reload configurations must belong to the resident owner and must not be writable by other accounts. An elevated resident acting for another account does not evaluate secret references during reload.

The address cache migrates from format 1 to format 2 and preserves current assignments. An older client refuses format 2; restore its format-1 cache from backup when rolling back, or stop the resident and regenerate its addresses.

gdsgate down

Asks a running resident client to stop, over its control channel (a socket on Linux, a named pipe on Windows). Exits 0 when no client is running.

gdsgate status [--json] [--explain] [--refresh]

What the resident client is doing: the identity it holds, the level it settled on, when the catalog was last read, how names are served, how access is established and how a refusal reaches you, the last failure, and one row per resource with the command that reaches it.

identity  identity live for 42m · direct · catalog last read 14s ago
names     hosts · zone gds in /etc/hosts · 3 answering
access    certificates: pre-issued · notify: /usr/bin/notify-send
state     /home/me/.gdsgate/endpoints.json

resource     name             kind        address          access   serving  connect
pg-prod      pg-prod.gds      postgres    127.70.0.2:5432  ok       yes      psql 'postgres://pg-prod.gds'
dev1         dev1             ssh connect         -                step-up  yes      -
prodcluster  prodcluster.gds  kubernetes  127.70.0.4:6443  ok       yes      kubectl --context prodcluster
pg-demo      pg-demo.gds      postgres    127.70.0.5:5432  ok       no       -

dev1     port 22 is privileged → set `[client.direct].privileged_port_offset = 10000` and restart the...
dev1     access refused 1m ago → run `gdsgate mfa grants create dev1 --action connect`, then connect
pg-demo  no connector is serving this resource → start or restart the Connector configured to serve `pg-demo`
# `gdsgate status --explain` prints each of these in full

The example uses Linux with [client.direct].privileged_port_offset = 0. The default offset is 10000, so SSH port 22 becomes local port 10022. On Windows the hosts file is C:\Windows\System32\drivers\etc\hosts.

Header lines:

Line Shows
identity identity lifetime, level, last catalog read
names how names are served
access how certificates are issued and how refusals are notified
state the state file and, at the transparent level, the interface and what it refused
error the last failed poll or file write; shown only then

ACCESS values:

Value Meaning
ok permitted
step-up, approval, mfa refused; the line under the table names the command that resolves it
denied refused; only an administrator can change it
? the gateway could not be asked
- not asked yet

SERVING is whether a connector served the resource at the last catalog read, according to the gateway's routing table. ACCESS ok alone does not mean reachable.

Value Meaning
yes served
no declared, nothing serving it; a connection is refused
- unknown: the listing did not come through a gateway

A resource with no listener, refused access, or no serving Connector has no connect command. Each problem appears under the table with its first action. The brief output shows up to five problems and counts the rest.

--explain shows each problem as separate fields. The recommended action comes first; alternatives are independent ways to address the same problem.

dev1: port 22 is privileged
  Fix:          set `[client.direct].privileged_port_offset = 10000` and
                restart the resident client
  Alternative:  `gdsgate ssh connect dev1`
  Alternative:  set `[client].access = "transparent"`; restart the resident
                client with interface privileges
  Cause:        could not bind 127.70.0.3:22: Permission denied (os error 13)

Fix is the first action, Alternative lists other actions, Cause lists individual facts, and Note reports any remaining usable access. The local connections section identifies observed processes; it does not authorize them.

An unsuccessful access check is reported as access check failed, with the original error under Cause. Possible causes include a Connector without a route to the resource or a transport failure. When the catalog and access check both report a missing Connector, one diagnostic includes the original access error and the Connector action. Independent access errors and policy refusals remain separate diagnostics.

Resources are ordered by availability: ready first, then resources needing attention, then unavailable resources. Unchecked access, failed checks, missing local listeners and required user actions need attention. Policy denials, missing Connectors and catalog withdrawals are unavailable. Within each group, resource IDs are sorted without case sensitivity, with the exact ID breaking ties. The table, --explain and JSON use the same order. Diagnostics for one resource stay together; resolver problems appear first.

In a terminal, ready resources are green, problems needing action are yellow, policy denials are red, and withdrawn resources are grey. Diagnostic headings use the same colour as their resource row. Only the target name is bold; the problem description after the colon uses normal weight. Set NO_COLOR=1 to disable colour. Files, pipes and JSON contain no colour escapes.

--json retains the status fields, including running, resource connect, listener.reason and resolver.detail. The top-level diagnostics array contains every problem shown by --explain, including access refusals and resources without a serving Connector. Its entries have these fields:

Field Meaning
target Resource ID or names for resolver diagnostics.
headline Short problem description.
cause Array of individual facts.
actions Recommended action first, followed by independent alternatives.
note Remaining usable access, or null.

--explain does not change JSON. A stopped client reports running: false and diagnostics: []. A running client without problems also has an empty diagnostics array. Both running and stopped status reports exit zero.

Flag Purpose
--json Machine-readable output, always complete.
--explain Print every reason under the table in full, unfolded.
--refresh Ask the client to read the catalog now, wait up to 15 seconds for the read, then report. Two reads are at least 5 seconds apart.

See Resident client for the whole picture.

gdsgate completion generate <bash|zsh|fish|powershell>

completion <shell> and completion --resources [--for <group>] remain previous calls. New resource queries use completion resources list.

Emits a completion script for the shell.

gdsgate completion generate bash > ~/.local/share/bash-completion/completions/gdsgate
gdsgate completion generate zsh  > ~/.local/share/zsh/site-functions/_gdsgate
gdsgate completion generate fish > ~/.config/fish/completions/gdsgate.fish

In PowerShell, load it from the profile:

Add-Content $PROFILE 'gdsgate completion generate powershell | Out-String | Invoke-Expression'

Besides commands and flags it completes resource names where one is expected, of the kinds the command takes: SSH servers after gdsgate ssh connect, databases after gdsgate db proxy, every resource after gdsgate requests create. The names come from the resident client's endpoints.json in ~/.gdsgate/; completion does not contact the cluster. A machine that has never run the resident client offers no names.

gdsgate completion resources list [--for ssh|db|kube|tcp|mcp|web] prints those names, one per line; the generated script runs it.

gdsgate identity bootstrap [--token <jwt> | --provision-token <t>]

Bootstrap a workload identity, the machine counterpart of login. Generates a key pair locally, exchanges a proof for a short-lived SPIFFE-named certificate, and caches it where the resource commands look for credentials. The private key stays on the host. Prints the SPIFFE ID, its owner and groups if any, and the validity.

Flag Purpose
--token <jwt> A platform id-token addressed to the gateway, for example --token "$GITLAB_OIDC" from a GitLab id_tokens: job.
--provision-token <t> An admin-minted provision token (gdsgate authority tokens create --role workload), the fallback for a host with no platform issuer.
--endpoint <url> Bootstrap listener URL. Defaults to [enroll].endpoint.
# In a GitLab CI job (id_tokens: configured for the gateway audience)
gdsgate identity bootstrap --token "$GDSGATE_OIDC"
gdsgate db proxy prod-db --listen 127.0.0.1:5433 &
psql -h 127.0.0.1 -p 5433

gdsgate identity renew [--endpoint <url>]

Renew a provision-token workload without a new token. The client presents its stored certificate, proves possession and moves to a new key. Platform-token workloads bootstrap again with a new token. machine-id --renew remains an alias.

identity bootstrap refuses --renew; select identity renew explicitly.

A separate join relay publishes its port and registration trust. join keeps the IP or domain used to reach the cluster and uses that port. It verifies registration with its own CA and SNI before sending a token. For NAT or an LB with a different registration port, set proxy.public_registration_endpoint on the Proxy.