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. What it writes:
| Path | Contents |
|---|---|
~/.gdsgate/config.toml |
[endpoints].proxy_public, [client] (anchor path, server name), the addressing defaults the cluster publishes, and [proxy].public_origin if it has one |
~/.gdsgate/transport-ca.pem |
the cluster's transport CA, which [client].transport_ca points at |
Both are owner-only and written atomically. Later commands read
~/.gdsgate/config.toml unless --config names another file.
connect shows the fingerprint of the offered anchor and asks for confirmation,
or compares it with --fingerprint. With neither a fingerprint nor a terminal it
stops. There is no trust-on-first-use. Once the anchor is confirmed, connect
fetches the settings again over a connection verified against it and writes
those.
The operator publishes the fingerprint with
gdsgate ca fingerprint:
Flags:
| Flag | Effect |
|---|---|
--fingerprint <SHA256> |
The expected anchor fingerprint. Separators and case ignored. A mismatch is an error. Required off a terminal. |
--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. |
connect does not sign in and leaves the login and certificate cache
unchanged; sign in afterwards with gdsgate login, or gdsgate machine-id 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.
gdsgate login [--browser]¶
Authenticate to the cluster.
- 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 identity token under~/.gdsgate/<cluster>/id_token, mode 0600. Later commands reuse 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.
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.
--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 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 - 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 step-up 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 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 <bash|zsh|fish|powershell>¶
Emits a completion script for the shell.
gdsgate completion bash > ~/.local/share/bash-completion/completions/gdsgate
gdsgate completion zsh > ~/.local/share/zsh/site-functions/_gdsgate
gdsgate completion 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, databases after
gdsgate db proxy, every resource after gdsgate request-access. 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 [--for ssh|db|kube|tcp|mcp|web] prints those
names, one per line; the generated script runs it.
gdsgate machine-id [--token <jwt> | --provision-token <t> | --renew]¶
Bootstrap or renew 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 create-token --role workload), the fallback for a host with no platform issuer. |
--renew |
Renew a provision-token workload without a token: presents the stored certificate with a proof of possession and moves to a new key. A platform-token workload runs the bootstrap again with a new token. |
--endpoint <url> |
Bootstrap listener URL. Defaults to [enroll].endpoint. |