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:
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,loginruns 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'sidentity.json, mode 0600. Later commands reuse or renew it. --browserruns Authorization Code with PKCE over a loopback redirect, so the browser opens on the same machine.- Without
[oidc],loginconfirms reachability and a local-dev principal is used (GDSGATE_USER, defaultdev).
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:
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:
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.