Skip to content

Administration commands

Authority command reference. Global flags, output channels, environment overlays and exit codes are on the CLI hub.

Entity/action commands

Administrative collections use entity action. The previous commands remain aliases with the same arguments, output, exit codes and permissions. Route and operator flags work before or after a nested action.

gdsgate authority nodes list --health --json
gdsgate authority resources list
gdsgate authority resources list --serving
gdsgate authority resources set demo-ssh --kind ssh
gdsgate authority policy proposals list
gdsgate --config /etc/gdsgate/authority.toml authority keys sync root status

Identity bootstrap requires --role admin or --role provisioner. Admin bootstrap accepts --ttl and uses the local store; provisioner bootstrap accepts --token and uses enrollment. Options for the other role are refused.

resources list --serving uses the tunnel-registration RPC. resources list reads the administrative catalog. The serving form accepts --endpoint and refuses --declaration.

Keyring rotation selects a step as a subcommand. --kind belongs to prepare; activation still requires acknowledgements and retirement still checks references.

gdsgate --config /etc/gdsgate/authority.toml authority keys rotation prepare control
gdsgate --config /etc/gdsgate/authority.toml authority keys rotation activate control
gdsgate --config /etc/gdsgate/authority.toml authority keys rotation retire control

Legacy aliases

Primary path Previous path
authority [no action] authority
authority tokens create authority create-token
authority identities bootstrap --role admin authority bootstrap-admin
authority identities bootstrap --role provisioner authority bootstrap-provisioner
authority ca rotate user-ssh authority rotate-ca
authority ca rotate onward-ssh authority rotate-onward-ca
authority ca rotate host-tls authority rotate-host-tls-ca
authority ca rotate spiffe authority rotate-spiffe-ca
authority ca rotate jwt authority rotate-jwt-ca
authority ca export onward-ssh authority onward-ca-pub
authority ca export onward-ssh --all authority onward-ca-pubs
authority nodes list authority list-nodes
authority offers list authority list-offered
authority nodes revoke authority revoke-node
authority nodes remove authority delete-node
authority users forget-name authority forget-user
authority workloads list authority list-workloads
authority workloads revoke authority revoke-workload
authority nodes import authority import-node
authority resources list --serving authority list-serving
authority resources list authority list-resources
authority resources set authority set-resource
authority scopes list authority list-scopes
authority scopes set authority set-scope
authority scopes remove authority remove-scope
authority agents list authority list-agents
authority agents set authority set-agent
authority agents switch authority switch-agent
authority agents remove authority remove-agent
authority resources remove authority remove-resource
authority epochs activate authority activate
authority profile set authority set-profile
authority audit verify authority verify-audit
authority store migrate authority migrate
authority policy proposals approve authority policy approve-edit
authority policy proposals reject authority policy reject-edit
authority policy proposals list authority policy pending-edits
authority keys sync tokens create authority keys sync create-token
authority keys sync root wrap authority keys sync wrap-root
authority keys manifest show authority keys backup-manifest
authority audit checkpoint-keys export authority keys checkpoint-keys
authority keys loss accept authority keys accept-loss
authority keys rotation prepare <ring> authority keys rotate <ring> --prepare
authority keys rotation activate <ring> authority keys rotate <ring> --activate
authority keys rotation retire <ring> authority keys rotate <ring> --retire
authority keys legacy migrate authority keys migrate-legacy
authority ca seeds wrap authority keys wrap-ca-seeds
authority ca keys prune authority keys prune-ca-keys
proxy [no action] proxy
setup [no action] setup
connector [no action] connector
authority keys sync root status authority keys sync root-status
authority keys sync root rotation prepare authority keys sync prepare-root
authority keys sync root rotation activate authority keys sync activate-root
authority keys sync root rotation cancel authority keys sync cancel-root

The detailed contracts below also apply to their aliases. No alias emits a deprecation warning. Help and completion present the primary paths.

Implemented entity/action path inventory

authority proxy setup connector agent-host service client-net all connect join login machine-id up down status completion ls request-access approve requests delegate delegations revoke delegation mfa step-up ssh ssh-config proxy-ssh db kube rdp tcp mcp web ca license session doctor config contexts

auto gateway certificate emergency store

gdsgate authority

Runs the Authority service: control-plane gRPC listener (mutual TLS once the cluster is registered), bootstrap registration listener (plaintext, authenticated by join token), policy engine, certificate authorities, audit chain.

Reads: store_url, [endpoints], [policy], [oidc], [ca_rotation], [ha], [discovery], [approvals].

gdsgate --config authority.toml authority

Sub-actions fall into three groups:

Group Sub-actions Route
Administrative (gRPC, decided by Cedar) create-token, the five rotate-*-ca commands, list-nodes, list-offered, revoke-node, delete-node, forget-user, import-node, list-workloads, revoke-workload, list-resources, set-resource, remove-resource gateway (a person) or certificate (control-plane certificate); see --route.
Personal (gRPC, caller from identity token only) policy commands against a running cluster, recordings gateway. policy pull and policy push also work on the emergency socket.
Direct-store (on the Authority host, against store_url) bootstrap-admin, the deprecated init-* key commands, verify-audit, audit list, migrate, onward-ca-pub / onward-ca-pubs None. audit list also accepts --route store.

policy validate and policy template touch neither the store nor the network and take no route. bootstrap-provisioner needs no store credentials and no prior identity: it obtains one at the enrollment listener and renews it over mTLS.

Declaring from a document

set-agent, set-resource and set-scope accept a TOML document with --from <file>; - reads the standard input. The table name is the object kind:

[[agent]]
id      = "tool-bot"
node    = "agents-01"
program = "/usr/local/bin/tool-bot"
can     = ["llm:call@models-prod"]

[agent.cage]
profile = "basic"

[[agent.faces]]
name   = "tool-bot-tools"
kind   = "mcp"
source = "port"
port   = 9000
tools  = ["read_ticket", "search_kb"]
gdsgate --config operator-workstation.toml authority agents set --from agents.toml
# updated tool-bot on agents-01 (running)

Each table is an array of tables. Keys are the flag names with dashes replaced by underscores, and defaults are the flag defaults. A face's tools are nested in the face.

Objects the file does not name are not changed. Remove an object with remove-agent, remove-resource or remove-scope.

--declaration on list-agents, list-resources and list-scopes prints the same form:

gdsgate --config operator-workstation.toml authority agents list --declaration > agents.toml
# edit agents.toml
gdsgate --config operator-workstation.toml authority agents set --from agents.toml

An agent's document includes version, the version of the declaration when it was printed. set-agent --from sends it, and the write is refused if the declaration changed since; see concurrent edits. For a document printed on another cluster, pass --ignore-version.

The lease and the creation time are not printed and are not accepted. Catalog rows a connector published into a boundary are not printed.

Refused Condition
Unknown key The document contains a key this build does not define.
Two kinds in one file The document contains tables of more than one kind.
Empty document The document declares no object.
Duplicate name Two objects in the document have the same name.
Flags beside --from A declaring flag is set together with --from. Flags left at their defaults are ignored.

Two identities may hold [admin].identity_dir: gdsgate-admin, issued by bootstrap-admin, and the narrower gdsgate-provisioner, issued by bootstrap-provisioner. The commands are the same under both; the policy grants each group different actions. See Policy → The starting policy.

--route <auto|gateway|certificate|emergency|store>

Selects the route an administrative sub-action uses to reach Authority. Accepted before or after any authority sub-action; a sub-action the route does not serve refuses it by name.

Value The call travels The caller is The audit row names
gateway the gateway's client-facing listener you, by the identity token your provider issued you, and the address you called from
certificate a direct dial to [admin].endpoint the control-plane certificate in [admin].identity_dir that certificate's subject
emergency the socket on the Authority host whoever can open the socket gdsgate-break-glass, with admin_channel=emergency-socket
store the local persistent store the operating-system account that can open it no audit row; only audit list accepts this route
Value Use Requires
gateway A person. Cedar decides over you and your provider groups; context.src_ip is your address. Disabling your account at the provider removes the access. gdsgate login and a policy granting your group the action. The starting policy grants four of the six.
certificate A machine (a control plane on a timer); a person when the gateway is unreachable (top rung of the lock-out ladder). [admin] with a control-plane identity.
emergency Policy admits neither of the above. Carries policy pull, policy push, list-nodes, list-workloads, list-resources, set-resource, remove-resource; refuses the rest by name. See Admin guide → Lock-out and recovery. Running on the Authority host.
store authority audit list only; reads local audit rows. Refused on every other sub-action. verify-audit is also local and has no route flag. Access to the store.

auto (default) takes certificate when the configuration has an [admin] section naming a control-plane identity, else gateway. A machine configured only as a client (gdsgate connect) takes gateway.

# a person, on their own machine: through the gateway, as themselves
gdsgate authority nodes list

# a control plane, on the workstation configuration: straight at Authority
gdsgate --config provisioner.toml authority nodes list

# either configuration, saying which route out loud
gdsgate --config provisioner.toml authority --route gateway list-nodes

The policy commands against a running cluster (pull, push, edit, approve-edit, pending-edits, history, diff, revert) and recordings have no certificate form. Under auto they take gateway; --route certificate is refused with a message. policy pull and policy push also work over emergency.

--operator <NAME>

Names the person a call is made for, on --route certificate. Used by a provisioning control plane (CN=gdsgate-provisioner) acting for a person its own log knows.

gdsgate --config provisioner.toml authority --operator anna.ivanova@provider.example \
    revoke-node nodes-abc123

The value is stored in a separate audit field, printed as unverified, and takes no part in policy decisions. Use the identifier your own log names the person by.

Rule On violation
Only on --route certificate. Refused by the command line, and by Authority.
Up to 128 bytes of ASCII letters, digits, space, or . _ - @ + '; no leading or trailing space; must not start with gdsgate- (any case). InvalidArgument; the call does nothing.
Peer is a control-plane identity. PermissionDenied.
Sub-action writes an audit row: create-token, the five rotate-*-ca commands, revoke-node, delete-node, forget-user, revoke-workload, import-node, list-resources, set-resource, remove-resource, audit list. Refused on any other sub-action.

audit list is audited on both outcomes. list-nodes and list-offered write a row only when refused, and take no --operator. --route store records nothing and takes no --operator.

gdsgate authority init --profile <dev|pilot|production> [--output <PATH>] [--store-url <URL>] [--store-password-file <PATH>] [--oidc-issuer <URL>] [--oidc-client-id <ID>] [--control-keyring <REFERENCE>] [--audit-keyring <REFERENCE>] [--kms-key <NAME=KIND:COMMAND>] [--if-missing] [--dry-run] [--json] [--approve-plan <DIGEST>]

First run of an Authority. Writes the configuration, puts the two integrity keyrings in place, and prints one JSON object to stdout. Non-interactive.

The run prints its plan first; --dry-run stops there. A dry run checks each keyring reference and creates nothing, so a profile that refuses to create a keyring in the open refuses at the dry run.

gdsgate authority init --profile pilot --output /etc/gdsgate/authority.toml --dry-run
# + file:/etc/gdsgate/control.keys — the control keyring is created in the open; …
# ! restoring the files afterwards does not undo this
# + file:/etc/gdsgate/authority.toml — the Authority configuration (profile pilot)
# ? the store this configuration names can be opened — …
# plan digest 5008bbe3065bc874

--approve-plan <digest> refuses unless the plan is the one that digest names. Exit codes are the setup contract; --if-missing keeps its own 3.

gdsgate authority init --profile pilot \
    --output /etc/gdsgate/authority.toml \
    --store-url postgres://gdsgate@db.example.internal/gdsgate
{
  "configuration": "/etc/gdsgate/authority.toml",
  "outcome": "written",
  "profile": "pilot",
  "transport_ca": { "fingerprint": "3f9c1a2b4d5e6f708192a3b4c5d6e7f8" },
  "activation": { "epoch": 2, "stamped": true },
  "keyrings": [
    {
      "keyring": "control",
      "source": "file /etc/gdsgate/control.keys",
      "created": true,
      "keys": ["a1b2c3d4e5f60718293a4b5c6d7e8f90"]
    },
    {
      "keyring": "audit",
      "source": "file /etc/gdsgate/audit.keys",
      "created": true,
      "keys": ["d4e5f60718293a4b5c6d7e8f90a1b2c3"]
    }
  ]
}

The summary holds no key material: fingerprints, references as written, the profile and a path.

Flag Default Purpose
--profile <dev\|pilot\|production> required Deployment profile. See [security].
--output <PATH> /etc/gdsgate/authority.toml on Linux; %ProgramData%\gdsgate\authority.toml on Windows Where the configuration is written.
--store-url <URL> unset Written into store_url. Use a postgres:// URL outside a laptop; put the password in store_password_file.
--store-password-file <PATH> unset Written into store_password_file. Read when connecting; the password stays out of the URL, the plan and logs.
--oidc-issuer <URL> unset Written into [oidc].issuer. Unset: identity tokens are verified with the built-in development issuer, whose secret every gdsgate binary carries; production refuses to start on it.
--oidc-client-id <ID> unset Written into [oidc].client_id.
--control-keyring <REFERENCE> unset Existing control keyring, in secret-reference notation. Unset: init creates one beside the configuration, except under --profile production.
--audit-keyring <REFERENCE> unset Existing audit keyring; same rules.
--kms-key <NAME=KIND:COMMAND> none External key, written into [[kms_keys]]. Repeatable.
--if-missing off Existing configuration: print its summary and exit 3. Without it: refuse, exit 1. Nothing is overwritten in either mode.

dev and pilot create a missing keyring with one active root key. production refuses to create a keyring on disk; name an existing one with --control-keyring / --audit-keyring, or create it in a key service.

--kms-key declares an external key; <name> is what a ${kms.<name>+file:PATH} reference in --control-keyring or --audit-keyring points at. The flag writes only the exec kind: <name>=exec:<program> <argument...>, split on whitespace (an argument with a space needs a wrapper script). Declare native key services in the configuration file; see [[kms_keys]]. With a ${kms.<name>+...} reference, production creates the keyring inside the key service.

init opens store_url and brings its schema up to date, as authority store migrate does, then persists the transport CA and prints its fingerprint. Without --store-url, transport_ca.fingerprint is null and the summary gives the reason.

A new cluster is stamped with the current activation epoch in the same pass. activation.epoch is the stored epoch; activation.stamped says whether this run set it.

Store Epoch
Empty (no catalogue, no enrolled nodes) Stamped with the current epoch by init, or by Authority at its first start (logged).
Holds resources or enrolled nodes Unchanged; the summary gives the reason. Raise it with authority epochs activate.
Only migrated with authority store migrate Not stamped.

At epoch 2 every ssh row in the catalogue declares its onward mode (gdsgate authority resources set <id> --kind ssh --onward-auth local|certificate|client_key); a row that declares none is not served.

gdsgate authority profile set --profile <dev|pilot|production> --changed-by <NAME>

Changes the deployment profile of an installed cluster. Rewrites the [security].profile line in the loaded configuration (comments and ordering are kept) and records the change on the audit chain.

gdsgate --config authority.toml authority profile set \
    --profile pilot --changed-by anna.ivanova
# authority.toml: `[security].profile` is now "pilot" (was "dev").
# Restart Authority for it to apply. The change is on the audit chain.
Flag Default Purpose
--profile <dev\|pilot\|production> required The profile to declare.
--changed-by <NAME> required Person the change is made for. Recorded on the audit row as asserted, unverified.

The profile decides whether the store may be a file, whether a listener may be plaintext, whether the all-in-one process starts, and whether keys accept-loss is allowed; see [security]. The audit row is written before the file. The new profile applies at the next restart; the command does not restart Authority. Exits 3 when the profile is already the requested one. --operator does not apply here.

gdsgate authority identities bootstrap --role admin --output-dir <DIR> [--ttl <secs>] [--if-missing]

With an empty --output-dir: opens the store directly, issues an admin-role provisioning token, registers with it, and writes cert.pem, key.pem and ca.pem. This is the only direct-store admin path. With a live identity in the directory: renews it over mTLS to [admin].endpoint; no second identity is issued and no token is spent.

gdsgate --config authority.toml authority identities bootstrap --role admin \
    --output-dir /var/lib/gdsgate/admin --ttl 300

Copy the three files to the operator workstation and point its config at them ([admin].identity_dir).

Flag Default Purpose
--output-dir <DIR> required Where cert.pem, key.pem and ca.pem are written. Required.
--ttl <secs> 300 Validity of the bootstrap provisioning token. It is spent at once; matters only if registration is interrupted.
--if-missing off Keep an existing identity this cluster accepts and exit 3; no token, no renewal, no file written. Without it, a live identity is renewed over mTLS.

With --if-missing the command is safe to repeat:

gdsgate --config authority.toml authority identities bootstrap --role admin \
    --output-dir /var/lib/gdsgate/admin --if-missing
# admin identity already present at /var/lib/gdsgate/admin (ttl 21h), nothing to do

Without --if-missing, a usable identity is renewed. A missing, expired, untrusted or otherwise unusable one is replaced through the bootstrap path, and the reason is printed. A renewal failure on a usable identity is an error; to force a new identity, remove the directory.

An identity is usable (and kept by --if-missing) when all of these hold; otherwise the run prints the failed condition and issues a new one:

  • cert.pem, key.pem and ca.pem are readable;
  • cert.pem parses and is within its validity window;
  • more than a quarter of the certificate's lifetime remains;
  • the certificate was issued by a CA this cluster trusts now;
  • ca.pem carries the CA the cluster serves with;
  • key.pem matches cert.pem.

The issued identity lives 24 hours, or [transport].cert_ttl_secs when set.

gdsgate authority identities bootstrap --role provisioner --output-dir <DIR> [--token <TOKEN>] [--if-missing]

Obtains the provisioning control plane's mTLS identity on the first run and renews it on later runs. Writes cert.pem, key.pem and ca.pem to --output-dir, the files [admin].identity_dir reads. authority sub-actions then run as CN=gdsgate-provisioner with what the policy grants Group::"gdsgate-provisioner".

The store is not opened; no database credentials are needed. The first run spends a one-time --role provisioner token at the enrollment listener; later runs renew over Authority.RenewIdentity.

# First run, on the provisioning control plane.
gdsgate --config provisioner.toml authority identities bootstrap --role provisioner \
    --output-dir /var/lib/gdsgate/provisioner \
    --token "$(gdsgate --config operator-workstation.toml \
        authority tokens create --role provisioner --ttl 300)"

# Every run after that — no token is read.
gdsgate --config provisioner.toml authority identities bootstrap --role provisioner \
    --output-dir /var/lib/gdsgate/provisioner --if-missing
Flag Default Purpose
--output-dir <DIR> required Where cert.pem, key.pem and ca.pem are written and read back. Required.
--token <TOKEN> [enroll].token One-time token from authority tokens create --role provisioner, for the first run only. Not read once --output-dir holds an identity.
--if-missing off Keep an identity with more than five minutes left (or more than a quarter of its lifetime, when that is less) and exit 3. Without it, every run renews; a renewal replaces the identity and its registry row.

Endpoints: [enroll].endpoint for the first registration; [enroll].renew_endpoint, else [endpoints].authority, for renewals. With an empty --output-dir and no enrollment endpoint, the run fails with a message.

Once --output-dir holds an identity, a failed renewal is an error and no token is spent. To replace a broken identity, remove --output-dir and run again with a fresh token. Two control planes registering at once end with one identity: the second finds the token spent.

The command prints the identity path, its validity, and the [admin] identity_dir line for this host's configuration.

gdsgate authority tokens create --role <ROLE> [--ttl <secs>]

Issues a role-bound permission. Node roles receive a one-time registration token; Authority/pair roles receive a restricted protected-configuration permission. The permission goes to stdout; logs go to stderr.

gdsgate --config operator-workstation.toml authority tokens create --role connector --ttl 3600
Flag Default Purpose
--role required proxy, connector, admin, workload, provisioner or agent-host for node enrollment; authority or pair for protected configuration. Required. An admin identity is normally issued by bootstrap-admin.
--ttl <secs> Node roles: 3600; Authority/pair: 600 Validity before the permission expires. Authority/pair range: 60..3600.
--owner <ID> Random assigned ID Authority/pair recipient override; refused for node enrollment.

For --role authority or --role pair, run on an existing Authority host:

umask 077
sudo gdsgate authority tokens create --role authority > authority.token

These permissions use the local installed Authority configuration and its protected distribution credential. --config selects an explicit local source; the chosen client context does not supply these settings. --operator and explicit --route are refused. The ID is assigned before joining; an individual peer is admitted only after its listener-bound proof. The lower-level authority keys sync tokens create --role authority|pair remains available. See Authority join.

The following routing and policy rules apply to node enrollment roles.

On --route certificate the configuration needs [admin].endpoint and [admin].identity_dir.

Decided by Cedar issueJoinToken over the identity the token creates: EnrolledNode::"gdsgate-connector", EnrolledNode::"gdsgate-proxy", EnrolledNode::"gdsgate-admin", and so on. The starting policy grants the provisioner connector, proxy, workload and agent-host. A refusal prints permission denied: policy denied admin action and exits non-zero. See Policy → Administrative actions.

gdsgate authority ca rotate user-ssh

Requests a User SSH CA rotation. Authority (the leader in HA) runs the paced double-signing rotation. Works regardless of [ca_rotation].enabled. The request is kept in the store until a rotation of that CA starts, so one made while Authority is stopped runs on the first check after it starts. The same holds for the other rotate-* commands.

gdsgate --config operator-workstation.toml authority ca rotate user-ssh

gdsgate authority ca rotate onward-ssh

Requests an Onward SSH CA rotation. This CA signs the OpenSSH user certificates a connector presents to a downstream sshd in the model-B jump-host path. Same paced rotation as rotate-ca. Add the new public key to TrustedUserCAKeys on every downstream sshd.

gdsgate --config operator-workstation.toml authority ca rotate onward-ssh

gdsgate authority ca rotate host-tls

Requests a HostTls (transport) CA rotation. Same paced double-signing rotation as rotate-ca. Nodes pick up the new trust bundle on their next mTLS renew. Set [ca_rotation].propagation_secs to at least the renew interval. HostTls has no automatic schedule.

gdsgate --config operator-workstation.toml authority ca rotate host-tls

gdsgate authority ca rotate spiffe

Requests a rotation of the SPIFFE CA, which signs workload certificates. Same paced rotation as rotate-ca. Connectors and proxies pick up the new SPIFFE bundle on their next mTLS renew.

gdsgate --config operator-workstation.toml authority ca rotate spiffe

gdsgate authority ca rotate jwt

Requests a rotation of the JWT-signing CA, which signs the Gdsgate-Identity assertion sent to an application when a kind = "web" backend sets identity = "signed". Same paced rotation as rotate-ca. Manual only. Set propagation_secs to at least the time applications cache the key set the Proxy publishes at /.well-known/gdsgate/jwks.json. See Configuration → Telling the application who came.

gdsgate --config operator-workstation.toml authority ca rotate jwt

gdsgate authority nodes list

Prints every enrolled node, active and revoked, one per row.

Columns: id, role, cn, fingerprint, enrolled, state. enrolled is UTC, YYYY-MM-DD HH:MM:SS; state is active or revoked <time>. fingerprint is the first 16 characters, or the whole digest under --wide.

<row>
    calls itself <name> (claimed by the node, unverified)

The second line appears for a node that set [enroll].node_name: a name the node claims, unverified. See Naming a node.

Pass the id to authority nodes revoke.

gdsgate --config operator-workstation.toml authority nodes list
# id        role       cn                  fingerprint       enrolled             state
# n-4f21a9  proxy      gdsgate-proxy       9f2c1d7b3e4a5061  2026-09-12 08:41:07  active
# n-77c0e3  connector  gdsgate-connector   1b8e44a0c5d92f37  2026-09-12 08:44:19  active
#     calls itself edge-frankfurt (claimed by the node, unverified)

--json prints one object per node, with times as Unix seconds and the whole fingerprint.

--health shows the health each node last reported through the periodic check (see [doctor]). Columns: id, role, ok, info, warn, fail, reported; --wide adds fingerprint. Degraded checks follow each row, indented, worst first. A node missing from --health has not reported since Authority last started.

gdsgate --config operator-workstation.toml authority nodes list --health
# id        role       ok  info  warn  fail  reported
# n-4f21a9  proxy      11  0     1     0     42s ago
#     warn  store-latency · p99 write 412ms over the last minute

Both forms are decided by Cedar viewNodes over EnrolledNode::"*", as is authority workloads list. A refusal prints permission denied: policy denied admin action and exits non-zero. Only refusals are audited.

gdsgate authority offers list

Prints what connectors say they serve, one per row.

Columns: node, resource, kind, offered, declared. offered is UTC, YYYY-MM-DD HH:MM:SS.

<row>
    offered by <name> (claimed by the node, unverified)

A connector states the resources it serves each time its reverse tunnel opens. declared=no needs an operator: the catalog has no row for the resource and nothing is routed to it until one is declared with authority resources set.

Resource id, kind and the indented node name are claimed by the connector, unverified. The node id, timestamp and declared come from the cluster.

declared=yes means the catalog has a row for the resource id. It says nothing about the node: a row is listed until ten minutes after that node last opened a tunnel, whether the node is still running or not.

Row Cause Action
An old node after a re-join, next to the new one its offer, from the last tunnel it opened; its registry row is still active authority nodes revoke <old id>: its offers go at once, and the new node's tunnel is admitted. Without it the offer goes ten minutes after the old node's last tunnel, and the Proxy drops the new node's tunnel until then.
A node that stopped the same none: the row goes after ten minutes

A backend added to [[connector.backends]] appears after the connector restarts.

gdsgate --config operator-workstation.toml authority offers list
# node      resource        kind      offered              declared
# n-77c0e3  db-orders_prod  postgres  2026-09-12 08:44:21  yes
# n-77c0e3  host-db7        ssh       2026-09-12 08:44:21  no

--json prints one object per offer, with offered_at_unix as Unix seconds.

Decided by Cedar viewNodes, as authority nodes list is.

gdsgate authority nodes revoke <id>

Revokes an enrolled node: it can no longer renew its transport leaf over mTLS. Prints revoked on first success, already revoked on a re-run, unknown (exit non-zero) if no such row exists. The revocation is sealed with the revocation key of the control keyring (see [security.integrity]); a database rollback does not undo it.

Every proxy drops the node's reverse tunnel at once and ends the sessions over it. Its resources stop being routable, and its next registration is refused.

The issued leaf stays chain-valid until not_after. For a service other than a gdsgate listener that trusts the leaf, rotate the HostTls CA.

NODE_ID="<id-from-list-nodes>"
gdsgate --config operator-workstation.toml authority nodes revoke "$NODE_ID"

gdsgate authority nodes remove <id>

Removes the registry row of a node revoked at least seven days ago. Prints deleted on success; anything else exits non-zero:

Printed Meaning
deleted The row is gone.
not revoked The node is still active; run revoke-node first.
too recent Revoked, but less than seven days ago. The message names the second from which the row may go.
unknown No such row; also printed by a re-run of a successful delete.

The HMAC revocation receipt is kept; the id stays revoked. The removal is audited as identity.delete, with the reason when refused. Join and revocation rows stay on the chain.

Decided by Cedar revokeNode on the node's EnrolledNode entity, the same action as revoke-node; a policy cannot grant one without the other.

NODE_ID="<id-from-list-nodes>"
gdsgate --config operator-workstation.toml authority nodes remove "$NODE_ID"

gdsgate authority users forget-name <subject>

Removes a person's display name from the user directory that display_name_claims fills. Audit listings show the subject again. The audit chain never held the name and is not changed. A later sign-in records the name again; to stop keeping names at all, set display_name_claims = [].

Printed Exit Meaning
forgotten 0 The name is gone.
unknown 1 No name was kept for the subject; a re-run prints this.

<subject> is actor_verified in audit list --json. Gated by Cedar disableUser on User::"<subject>". Written to the audit chain as user_name.forget (AUT-0401) on every outcome, the refusal included.

gdsgate --config operator-workstation.toml authority audit list --actor alice@example.com --json --limit 1 |
  jq -r .actor_verified
# 8f3a1c2e-5b7d-4e19-a0c6-3d2b1f4e5a67
gdsgate --config operator-workstation.toml authority users forget-name 8f3a1c2e-5b7d-4e19-a0c6-3d2b1f4e5a67
# forgotten

gdsgate authority nodes import --role <ROLE> --cert <PATH>

Registers a node whose transport leaf was issued by a release older than the per-node revocation registry. Reads the leaf PEM, checks that its CN is gdsgate-{role}, and registers the SHA-256 of its SubjectPublicKeyInfo under the admin identity from [admin].identity_dir.

Prints imported <id> on the first run and existing <id> (exit 0, no duplicate row) on a re-run. Audited as identity.import (AdminAction) with the admin CN as actor.

After import the node renews normally. Without import, renewals from a pre-registry leaf are refused.

gdsgate --config operator-workstation.toml authority nodes import \
    --role connector \
    --cert /var/lib/gdsgate/connector.cert.pem

Nodes enrolled by the current release are registered at enrollment and need no import.

gdsgate authority workloads list

Prints every registered workload (provision-token machines tracked for token-less renewal), one per row.

Columns: spiffe id, owner, fingerprint, joined, state, cert until. state is active or revoked, and cert until is the leaf's expiry on an active row and the revocation time on a revoked one. fingerprint is the first 16 characters, or the whole digest under --wide.

Platform-token workloads keep no row and are not listed. Decided by Cedar viewNodes over EnrolledNode::"*", as authority nodes list is.

gdsgate --config operator-workstation.toml authority workloads list
# spiffe id                             owner        fingerprint       joined               state   cert until
# spiffe://gds.dev/ns/prod/sa/importer  platform-ci  0a71cc93b5e28d4f  2026-09-01 11:02:44  active  2026-09-21 14:10:00

--json prints one object per workload, with times as Unix seconds.

gdsgate authority workloads revoke <spiffe-id>

Revokes a registered workload. Prints revoked on first success; a re-run or an unknown id reports a no-op. The revocation is sealed with the revocation key of the control keyring (see [security.integrity]); a database rollback does not undo it.

Every proxy ends the workload's open sessions at once. Its next connection and next renewal are refused on every lane. Other sessions on the same connector tunnel continue.

The issued certificate stays chain-valid until its not_after. To invalidate certificates fleet-wide, rotate the SPIFFE CA.

Platform-token workloads keep no registry row and cannot be revoked here; deny them in policy.

WORKLOAD_SPIFFE_ID="spiffe://gds.example/provision/8f3c1234"  # from `list-workloads`
gdsgate --config operator-workstation.toml authority workloads revoke \
    "$WORKLOAD_SPIFFE_ID"

gdsgate authority resources list

Prints the whole resource catalog, one row per declared resource, in id order, including resources nobody may reach. gdsgate resources list shows what the caller may open.

Columns: id, kind, project, env, serving, hostname, port. A field the row does not declare is -; defaults are not resolved (see set-resource; gdsgate resources list shows the resolved name and port). An empty catalog prints the header and (none).

gdsgate --config operator-workstation.toml authority resources list
# id              kind      project   env   serving  hostname  port
# db-orders_prod  postgres  payments  prod  yes      orders    5433
# host-db1        ssh       payments  prod  yes      -         -

--wide adds the columns the table leaves out: aliases, native name, parent, approvers, ssh profile, web profile, onward auth, pinned, scope, lease until, labels. Labels are sorted by key; aliases keep declaration order. Columns do not depend on terminal width.

--json prints one object per resource. An undeclared field is absent (a row with no port has no port field). A row a connector wrote into a scope carries scope and lease_until (Unix seconds). An empty catalog prints nothing.

--declaration prints the declarations set-resource --from accepts, without cluster-added fields and without rows connectors published into a scope. See declaring from a document.

gdsgate --config operator-workstation.toml authority resources list --json
# {"id":"host-db1","kind":"ssh","project":"payments","environment":"prod","serving":"yes","port":22}

serving says whether a connector's tunnel is registered for the resource at that moment. It is filled on --route gateway and absent on the direct routes. An absent serving means unknown.

list-resources, set-resource and remove-resource are decided by Cedar manageCatalog over the resource's entity and audited on both outcomes. A refusal names the manageCatalog action and the policy to permit it in, and exits non-zero.

gdsgate authority resources list --serving

Prints the resources a connector's tunnel is registered for at that moment, one id per row under a resource header. An empty answer prints the header and (none). --json prints one {"resource":"<id>"} object per row, and nothing when nothing is served.

The call goes to the gateway's internal listener ([endpoints].proxy_internal, override with --endpoint) with the control-plane certificate from [admin].identity_dir. The gateway refuses a peer whose CN is not a control-plane role of this cluster. The client-facing listener is not used.

gdsgate --config provisioner.toml authority resources list --serving
# resource
# gitlabwww

For a person, the same answer is under serving in list-resources --route gateway --json.

gdsgate authority resources set <id> --kind <KIND>

Declares a resource in the catalog or rewrites an existing one. Prints created or updated and the row as written, in list-resources shape.

A rewrite replaces labels, parent, threshold and addressing. An omitted SSH onward mode preserves the existing value; a new SSH resource defaults to local. Use --onward-auth clear to remove the declaration. In a TOML [[resource]] import, onward_auth = "" explicitly clears it. Existing SSH resources without a mode stay unchanged until you choose a mode after checking their Connector backends.

gdsgate --config operator-workstation.toml authority resources set db-orders_prod \
    --kind postgres --project payments --env prod \
    --hostname orders --alias orders-db --port 5433 \
    --label tier=gold --parent host-db1 --min-approvers 2
# created db-orders_prod  postgres  project=payments  env=prod  hostname=orders  aliases=orders-db  port=5433  min_approvers=2  labels=tier=gold  parent=host-db1
Flag Default Purpose
--kind required ssh, postgres, mysql, kubernetes (or k8s), tcp, mcp, llm, web, web-egress, rdp. A CockroachDB backend is catalogued as postgres; cockroach is a backend kind in [[discovery.resources]] and [[connector.backends]] only.
--project default Owning project.
--environment / --env dev Environment; matched by the [approvals] per-environment cascade and by policies.
--label <KEY=VALUE> not set Label; repeatable. Labels become the resource's Cedar entity tags.
--parent <ID> not set Parent resource id, for example a database's host server. Unset: top-level.
--min-approvers <N> 0 Resource-level JIT approval threshold, the narrowest level of the approvals cascade. 0: [approvals] decides.
--hostname <NAME> not set Name clients address the resource by. Unset: the resource id, when it is a legal name.
--alias <NAME> not set Additional name, same rules; repeatable.
--port <N> 0 Port clients dial. 0: the kind's well-known port.
--pin-tool-descriptors off mcp only. Pins the server's tool descriptors; see Pinning tool descriptors. A write without the flag turns pinning off. Refused on other kinds.
--ssh-profile <PROFILE> not set ssh only. general: anything the policy allows. git: the four recognised git commands only; a shell, any other exec and every subsystem are refused before policy. Unset reads as general and shows as unset in list-resources. A write without it clears it. Refused on other kinds or other values.
--onward-auth <MODE> not set ssh only. local: session on the connector's own host, no further connection. certificate: jump host with the onward certificate Authority issues per connection. client_key: jump host authenticated with the person's key through their agent. Omitted: a new SSH resource uses local; an existing resource retains its mode. clear removes the declaration explicitly. A connector whose file names a different mode stops serving the resource.
--web-profile <PROFILE> not set web only. general: anything the policy allows. git: recognised git operations only; everything else is refused before policy. Unset reads as general. Refused on other kinds or other values.
--native-name <DOMAIN> not set web only. The application's own domain (app.example.com), as in [[connector.backends]] url. At least two labels; unique in the same namespace as --hostname and --alias. A resident client answers to it only when its user lists it in [client].intercept_names. Refused on other kinds. See Resident client → An application's own name.
--from <PATH> not set Read the declaration from a TOML document; - reads standard input. Not combined with other declaring flags. See declaring from a document.

Addressing follows the same rules as hostname, aliases and port in [[discovery.resources]]:

  • A name is one DNS label: 1 to 63 bytes of letters, digits and -, not starting or ending with -, folded to lowercase. An invalid name is refused.
  • The default name is the resource id when the id is a legal label. An id with _ or . (db-orders_prod) has no default name and needs --hostname to be addressable.
  • Names are unique across the catalog: hostnames, aliases, native names and id-derived names share one namespace. A taken name is refused, naming its holder. To move a name, remove it from the holder first.
  • The port defaults by kind: ssh 22, postgres 5432, cockroach 26257, mysql 3306, kubernetes 6443, web 80. tcp, mcp, llm and web-egress must declare one. This is the port clients connect on; the backend address is set on the connector.

Refused before sending: an unknown kind; a label that is not key=value, has an empty key or value, or repeats a key. Refused by Authority: a --parent naming the resource itself.

A resource also declared in [[discovery.resources]] is overwritten from the file at the next Authority start. Keep [discovery] empty on a cluster whose catalog is managed with this command.

gdsgate authority epochs activate --epoch <n>

Switches on a gate, cluster-wide. Irreversible.

Option Default Purpose
--epoch <N> required 1: Cedar action sshProgram is evaluated for every program an SSH session runs (shell, exec, subsystem). 2: an SSH resource's onward_auth is read from the catalog only; SSH resources without it are not served. A value below the current epoch is refused.
--force off Proceed despite the readiness shortfalls named, for example a node that can no longer be reached. The override is written to the audit chain.

Before epoch 1, sshProgram is not evaluated. Write sshProgram rules before activating epoch 1; without them every SSH program is denied.

Epoch 1 readiness: every node that serves an SSH resource and has renewed its activation lease recently must report support for the gate. Otherwise the call is refused, naming the nodes, and nothing changes. A node that has never renewed is listed as not ready. Upgrade it, revoke it with authority nodes revoke, or pass --force.

Epoch 2 readiness: every SSH resource names an onward_auth; the refusal lists those that do not. Run gdsgate --config <authority-config> doctor first: the state.resource.modes lines show each SSH resource, the catalog's value, and what each serving connector proposes. Set missing values with authority resources set --onward-auth.

After activation:

Event Result
Activation Live SSH sessions on every connector end; tunnels drop and reconnect; callers reconnect.
A node registers without a receipt for the gate (new, returning, or rolled back to an older binary) Served no SSH resource; reason gate_not_offered.
Epoch 2, SSH resource without confirmed mode Withheld; reason mode_not_confirmed.
Connector's epoch answer expires It stops serving SSH, ends its SSH sessions with the reason, and reopens its tunnel.
Connector sees an epoch below the highest in <state_dir>/activation.json Refused.
Connector serving SSH cannot read or write activation.json Does not start.

Authority issues a one-time receipt on each activation-lease renewal and spends it at the registration that presents it. A freshly started connector registers once without a receipt, renews, and registers again: the log shows a second tunnel a second later. The first tunnel stays open with no resource bound when every resource was withheld for gate_not_offered; a registration refused for another reason is dropped.

A compromised proxy can spend a relayed receipt and leave the connector with gate_not_offered. It cannot be served in the connector's place: a tunnel is admitted on the connector's own mutual-TLS leaf.

To correct a mistaken activation, write the rules the gate requires.

gdsgate --config operator-workstation.toml authority epochs activate --epoch 1

gdsgate authority resources remove <id>

Removes a resource from the catalog. Prints removed <id> or no such resource <id>; exits 0 in both cases.

Removal closes access and removes the connector binding and published inventory in one transaction; see Admin guide → What removing a resource does. A resource still declared in [[discovery.resources]] comes back at the next Authority start.

gdsgate --config operator-workstation.toml authority resources remove db-orders_prod

A row a connector wrote inside a scope carries scope=<prefix> (its scope) and lease_until=<unix seconds> (lease expiry). A declared row carries neither.

gdsgate authority scopes list

Prints every auto-scope, one per row, with its live rows against max_live. Rows with an expired lease are not counted.

Columns: prefix, node, kinds, onward, env, project, rows, lease, fallback, state. --wide adds labels and accepts. onward lists the onward modes an ssh target may name; - when the scope admits no ssh.

gdsgate --config operator-workstation.toml authority scopes list
# prefix  node         kinds         onward       env   project  rows     lease  fallback  state
# test-   kube-conn-1  postgres,ssh  certificate  test  qa       42/2000  900s   parent    enabled

A scope whose state is disabled accepts no publishes and holds no rows.

--json prints one object per scope. --declaration prints what set-scope --from takes back; see declaring from a document.

gdsgate authority scopes set <prefix> --node <ID> --kind <KIND> --env <ENV>

Creates or rewrites an auto-scope. The named connector writes catalog rows inside it; see discovery configuration.

A rewrite replaces the whole declaration; omitted values revert to defaults. A prefix that overlaps another scope or covers a declared resource is refused.

gdsgate --config operator-workstation.toml authority scopes set test- \
    --node kube-conn-1 --kind postgres --kind ssh --project qa --env test \
    --onward-auth certificate --label origin=k8s-auto --accept-label-key app \
    --max-live 2000 --lease-secs 900
# created test-  node=kube-conn-1  kinds=postgres,ssh  env=test  project=qa  rows=0/2000  lease=900s  fallback=parent  labels=origin=k8s-auto  accepts=app
Flag Default Purpose
--node <ID> required Enrolled node id (from authority nodes list) of the connector allowed to publish here, matched by key fingerprint.
--kind <KIND> required Kind a target may be published as; repeatable. A snapshot with another kind is refused.
--project <NAME> default Project written on every row in this scope.
--environment / --env required Environment written on every row in this scope. Not taken from the source.
--label <KEY=VALUE> not set Label written on every row; overrides a source label with the same key. Repeatable.
--accept-label-key <KEY> not set Label key accepted from the source; repeatable. Other keys are dropped; pin_src_ip is always dropped.
--max-live <N> 1000 Maximum live rows in the scope, including unexpired rows from earlier snapshots. A snapshot that would exceed it is refused in full. A scope declared in the configuration file is rewritten from [[discovery.auto_scopes]] at every Authority start; set the value there too.
--lease-secs <N> 900 How long a row stays after the snapshot that carried it.
--fallback <parent\|deny> parent Meaning of an absent row. parent: existing rules decide (a database sub-resource is authorized through its host). deny: a target without a row is not served.
--ssh-host-key <annotation\|tofu> annotation Source of an ssh target's host-key fingerprint. annotation: the gdsgate.io/ssh-host-key annotation; a target without it is not served. tofu: the first key seen is accepted for the lease.
--onward-auth <local\|certificate\|client-key> not set Onward mode an ssh target may name; repeatable. Required with --kind ssh, refused without it. A snapshot naming another mode is refused in full.
--from <PATH> not set Read the declaration from a TOML document; - reads standard input. Not combined with other declaring flags. See declaring from a document.
--disabled off Create the scope without accepting publishes. Disabling a scope that holds rows deletes them.

gdsgate authority scopes remove <prefix>

Removes an auto-scope and its rows, printing how many rows were deleted. Prints no such boundary <prefix> when there was none; exits 0 in both cases.

gdsgate --config operator-workstation.toml authority scopes remove test-
# removed test- (rows dropped: 42)

To stop publishes and keep the scope declared, use set-scope --disabled; its rows are deleted.

gdsgate authority agents list [--node <id>]

Lists declared agent runs and the state each agent host last reported. Decided by Cedar viewAgents.

Columns: agent, node, project, env, state, seen, cage, can, faces, lease. --wide adds program, groups, limits, on expiry and generation.

state is the declared state. seen is the state the agent host last reported, with its age.

can lists the capabilities the agent may use. faces lists the published faces as <published name>=<source>; - means none.

gdsgate --config operator-workstation.toml authority agents list --node agents-01
# agent        node       project  env   state    seen             cage    can                                 faces                    lease
# support-bot  agents-01  crm      prod  running  running 4s ago   strict  db:read@pg-crm,llm:call@models-prod  support-bot-console=pty  14m
# tool-bot     agents-01  crm      prod  running  exited×7 2s ago  strict  db:read@pg-crm                       tool-bot-tools=port      13m
seen Meaning
- No report from the host yet, or the host stopped reporting.
running The process is running and its endpoints are open.
frozen The lease expired with on expiry = freeze: the process runs, and all its access is closed.
exited The process exited without a stop request.
failed The declaration says running; the process is not: the start failed, or the lease expired with on expiry = stop. The start error is in the agent host's log.
stopped Declared stopped and stopped.

exited×7 is the number of consecutive unrequested exits. (old) marks a report about an earlier version of the declaration, or about a removed agent with the same name. After a host stops reporting, its last report stays in the column with a growing age.

seen is - for every agent when the Proxy relaying the call is older than Authority. Upgrade the gateways with Authority, or use --route certificate.

expired in the lease column means no host renews the lease. The declaration is kept.

--json prints one object per agent: the faces in full (name, kind, source, and port or path), the lease expiry, and the host report as seen, seen_at_unix, seen_generation, seen_incarnation, seen_exits and seen_exited_at_unix. The report fields are absent when there is no report. incarnation is the declaration's incarnation.

--declaration prints the declarations in the form set-agent --from reads, with version and without the lease. See declaring from a document.

Flag Default Purpose
--node <id> every node Only the runs declared for one agent host.

gdsgate authority agents set <id> --node <id> --program <path> --can <capability>

Creates or replaces an agent declaration. Decided by Cedar declareAgent. Agents run on Linux agent hosts only; an agent host on Windows starts but runs no agents.

gdsgate --config operator-workstation.toml authority agents set support-bot \
    --node agents-01 --project crm --program /usr/local/bin/support-bot \
    --can db:read@pg-crm --can llm:call@models-prod \
    --group agents-support --memory-max 2G --cpu-quota 200%
# declared support-bot on agents-01 (running)

The agent host applies the declarations it holds. Starting, stopping, granting a resource and removing one are all changes to the declaration.

Flag Default Purpose
--node <id> required Agent host that runs the agent, from list-nodes.
--project <id> default Project: the Cedar parent of the agent's principal.
--environment <env> prod Environment of the agent. Alias --env.
--group <name> none Cedar group of the agent's principal. Repeatable.
--can <capability> required unless a face is declared Capability the agent may use, e.g. db:read@pg-crm. Repeatable. A declaration without --can and without --face is refused.
--program <path> required Absolute path of the program.
--arg <value> none Program argument. Repeatable.
--face <spec> none Published face, <kind>:<source>[:<where>][=<name>]. Repeatable. See faces.
--tool <face>=<tool> none Tool an mcp face allows. Repeatable. An mcp face with no tool is refused.
--input-request <face>=<ask> none Server-to-client request the face's server may make: sampling/createMessage, elicitation/create, roots/list. Repeatable.
--from <PATH> not set Read the declaration from a TOML document; - reads the standard input. See declaring from a document.
--lease-secs <n> 900 Lease duration. The host renews the lease while the agent runs.
--on-expiry <what> freeze On lease expiry: freeze closes the agent's access and keeps the process; stop ends the process.
--sandbox <profile> strict Cage profile: none, basic or strict. Under none the agent still runs in its own network namespace, where its endpoints are. The run's record lists the restrictions applied.
--memory-max <size> none Memory limit, e.g. 2G.
--tasks-max <n> none Process count limit.
--cpu-quota <percent> none CPU limit; 100 is one core.
--stopped off Declare the agent stopped.
--if-version <version> not set Write only if the declaration is at this version (<generation>:<incarnation> from list-agents --declaration), or new if it must not exist yet.
--ignore-version off With --from: ignore version in the document.

Editing a declaration does not renew the lease.

Concurrent edits

set-agent replaces the whole declaration. A write that names the version it was edited from is refused if the declaration changed since:

gdsgate --config operator-workstation.toml authority agents list --declaration --node agents-01 > agents.toml
# version = "4:0f2e9a61-5b7c-4d1e-9a3f-2c8b1d4e6f70"; edit agents.toml
gdsgate --config operator-workstation.toml authority agents set --from agents.toml

A write without a version is accepted or refused according to [authority].agent_declaration_version (optional or required). A new declaration needs no version. switch-agent and remove-agent take no version.

Error contains Cause What to do
changed after this edit was made from it The declaration was rewritten, switched, or removed and declared again after the version was read. Print the declaration again and repeat the edit.
is already declared new was given for an existing agent. Edit the existing declaration.
is not declared The version refers to a removed agent. Declare it without a version.
agent_declaration_version = "required" No version was given for an existing agent. Use the document from list-agents --declaration, or pass --if-version.
Faces

A face publishes one endpoint of the agent as a catalog row: a source inside the cage and the resource kind it is published as. An agent can have several faces.

gdsgate --config operator-workstation.toml authority agents set support-bot \
    --node agents-01 --program /usr/local/bin/support-bot --can llm:call@models-prod \
    --face ssh:pty \
    --face web:port:8443=support-bot-console \
    --face mcp:port:9000=support-bot-tools \
    --tool support-bot-tools=read_ticket \
    --tool support-bot-tools=search_kb
Source Where Serves Note
pty nothing ssh The agent's console. One per agent.
port port number web, llm, mcp, tcp A port the agent listens on inside its cage.
unix path web, llm, mcp, tcp A socket in the agent's workspace. Refused with --sandbox strict.

stdio is refused as a source. Run an MCP server on a port or a socket.

Without =<name> the name is <agent>-<kind>, then <agent>-<kind>-2 for the second face of the same kind. The catalog row name is the boundary's id_prefix followed by the face name.

A web or llm face

Clients reach the face like any web or llm resource; each request is decided by httpRequest or llmCall. The agent listens inside its network namespace and is not reachable from the host network.

Requests reach the application with Host: <row>.invalid, where <row> is the catalog row name. The name does not resolve.

Settings of a web or llm face:

Setting On a face
Routes none named
Permit window five seconds
Audit sampled
Request body no ceiling
Caller's Authorization passed through, unless the face states identity (below)
WebSocket and other upgrades admitted, and decided by httpRequest with context.upgrade
Models an llm face serves not listed: Cedar decides each call
Provider credential on an llm face none

Traffic between the agent host and the agent is plain HTTP inside the host.

Telling a web face who opened it

With identity = "signed" on a web face, each request to the agent carries a gdsgate-identity header: a short-lived assertion signed by Authority with the caller's name, groups and the face. The caller's Authorization header is removed.

[[agent.faces]]
name     = "page-bot-web"
kind     = "web"
source   = "port"
port     = 8080
identity = "signed"
identity Meaning
none (default) No identity header.
signed A signed assertion on every request. web faces only.
headers Refused on a face.
Acting for the person who opened the page

The agent forwards the assertion it received, unchanged, on its own call. The call is allowed only if both the agent and the person may make it. The call's audit row has owner set to the person and the detail keys on_behalf_of (the person) and arrived_at (the face the assertion was issued for).

Resource kind Where the agent puts the assertion Audit row
llm gdsgate-identity request header llm.call
mcp params._meta["gdsgate/identity"] of tools/call, resources/read, prompts/get or tools/list. The backend needs enforce_tool_policy. tools/list returns the tools both may see. mcp.tool_call, mcp.resource_read, mcp.prompt_get
web, web-egress gdsgate-identity request header. Such requests do not use the permit window. http.request, written for every such request
kubernetes gdsgate-identity request header, including exec, attach and port-forward kube.request

SSH, TCP and database resources do not accept an assertion.

The assertion is not passed to the model provider, the application, the MCP server or the Kubernetes API server. The _meta entry is removed from the MCP message and the message is re-encoded.

Symptom Cause What to do
403 or a JSON-RPC error on a call the agent can make without the assertion The person may not make the call. Grant the person the action, or call without the assertion.
JSON-RPC error naming enforce_tool_policy The MCP backend has no per-call decision. Set enforce_tool_policy = true on the backend.
JSON-RPC error naming gdsgate/identity The _meta entry is not a non-empty string. Pass the assertion as received.
403, row reason = assertion_for_another_resource The assertion was issued for a face this agent does not serve, or for a face name two agents on the node share. Forward only the assertion received on the agent's own face; give each face a unique name.
403, row reason = assertion_not_for_a_person The assertion is about a workload or about a person acting under a delegation (--as). None: only a person acting for themselves can be acted for.
403, row reason = assertion_not_verifiable Not signed by this cluster, or expired. An assertion is valid for at most 60 seconds and not past the visitor's certificate. Forward the assertion as received, without delay.
400 from the model, web or Kubernetes lane The header is empty, not text, or repeated. Send it once, as received.
403 naming a per-request decision, from the web or Kubernetes lane The backend has no per-request gate. Serve the resource from a connector with an Authority client.
403 with a hint naming the field, or a refused tools/list Authority is older than this release. Upgrade Authority.
Tools on an mcp face

--tool <face>=<tool> lists the tools a caller may invoke through the face. Calls to other tools are refused. A face with no tool is refused at declaration. Each call is also decided by Cedar mcpCallTool; both must allow.

--input-request <face>=<ask> allows one of the three server-to-client requests. None are allowed by default.

Refused ports and sockets

A port from 14500 to 14500 + <resources> + 8 is reserved by the cage for the agent's endpoints and is refused as a face port. A unix face is refused with --sandbox strict; use a port or --sandbox basic.

gdsgate authority agents switch <id> [--stop]

Starts or stops a declared agent. Changes only the declared state. Decided by Cedar switchAgent (separate from declareAgent).

gdsgate --config operator-workstation.toml authority agents switch support-bot --stop
# support-bot is now stopped

Prints was already stopped when the agent is already stopped.

gdsgate authority agents remove <id>

Removes an agent declaration. Decided by Cedar declareAgent. Prints no agent run is declared under <id> when there was none; exits 0 in both cases.

gdsgate --config operator-workstation.toml authority agents remove support-bot
# removed support-bot

To stop an agent and keep its declaration, use switch-agent --stop.

After a removal, credentials issued to the agent are refused. An agent declared again under the same name gets new credentials. Moving an agent to another host invalidates the credentials on the previous host.

What an edit of a running agent does:

Edit Effect
Capability added, groups, faces Applied to the running process. Open sessions stay.
Capability removed, or a verb narrowed (db:write to db:read) The process is restarted. Its open sessions end.
Program, arguments, cage profile, limits The process is restarted. Its open sessions end.

gdsgate authority ca export onward-ssh

Prints the active Onward SSH CA public key in OpenSSH wire format, one line, the form TrustedUserCAKeys expects.

gdsgate --config operator-workstation.toml authority ca export onward-ssh \
    > /etc/ssh/gdsgate-onward-ca.pub

gdsgate authority ca export onward-ssh --all

Prints every persisted Onward SSH CA public key (active, retiring, candidate), one OpenSSH line each, newest generation first. Use it for the downstream sshd's TrustedUserCAKeys; certificates signed by the retiring generation keep verifying during rotation.

gdsgate --config operator-workstation.toml authority ca export onward-ssh --all \
    > /etc/ssh/gdsgate-onward-ca-bundle.pub

gdsgate authority keys

Sub-actions over the two integrity keyrings and the CA signing seeds. None prints key material: output is fingerprints, states, counts, public keys and references as written. sync create-token prints a secret first-boot permission on stdout; redirect that command to a protected file.

Sub-action Does
migrate-legacy Imports the five [audit].*_key_path keys into the rings.
wrap-ca-seeds Wraps stored CA signing seeds under a wrapping key.
prune-ca-keys Removes private keys of unused CA generations.
status Lists keys per ring, their use, and rows naming them.
verify Checks each key against its fingerprint.
backup-manifest Lists what a database backup needs beside it.
accept-loss Records a key as lost.
rotate Moves a ring to a new key.
checkpoint-keys Prints the public keys that verify signed checkpoints.
sync create-token Issues an expiring owner-bound Authority bootstrap permission.
sync root-status Shows root epoch, preparation ID and retained-owner acknowledgements.
sync prepare-root Prepares an independent root and an explicit retained membership.
sync activate-root Commits the prepared root after every retained owner acknowledges it.
sync cancel-root Abandons a preparation using a retained owner's independent credentials.
sync wrap-root Migrates this host's root to its declared wrapping provider.
gdsgate authority keys sync root-status|prepare-root|activate-root|cancel-root

Run on a provisioned Authority as the local administrator, using its protected root, receiver credentials and shared store. Root rotation changes the credential that encrypts distributed integrity material. Existing control and audit keys remain available for verification.

gdsgate --config /etc/gdsgate/authority.toml authority keys sync root rotation prepare
gdsgate --config /etc/gdsgate/authority.toml authority keys sync root status
gdsgate --config /etc/gdsgate/authority.toml authority keys sync root rotation activate
Command Input Result
prepare-root Optional repeated --withdraw <OWNER>; retain all current owners by default Signed preparation with independent deliveries to retained owners. Requires at least two retained owners and retains the initiating owner.
root-status None Current root epoch and material revision; pending preparation ID, initiator and waiting/acknowledged per retained owner. No secret material.
activate-root None; run on the initiating Authority Atomically commits the successor root and membership after all required signed durable ACKs. Running retained Authorities install it automatically.
cancel-root None; run on any retained Authority Signed cancellation advances material revision without changing root or membership. Replicas discard the cancelled preparation journal.

An offline retained member blocks activation. Restore it and wait for polling, or cancel and prepare an explicitly reviewed retained set. If the initiating Authority is lost, a retained owner can cancel, then prepare a new transition as initiator. Timeouts never remove members or cancel a preparation.

During preparation, ordinary keyring edits, membership additions and new bootstrap permissions are refused. A committed withdrawal stops the removed Authority; restarting it with its old credential is refused. Remove it from caller failover lists and service management separately. Root withdrawal does not revoke previously known CA keys, control/audit keys or DB/KMS credentials. See the root rotation procedure.

gdsgate authority keys sync tokens create

Run on a provisioned Authority host as the local administrator. Requires ha.keyring_sync.enabled, its protected distribution root and access to the shared store. The command writes a secret permission to stdout. It creates no service or local identity on the joining host.

umask 077
gdsgate --config /etc/gdsgate/authority.toml authority keys sync tokens create \
    --owner authority-b --ttl-secs 600 > /etc/gdsgate/authority-bootstrap.token
Flag Default Contract
--owner <NAME> Required Stable ha.owner of the joining Authority; 1–128 ASCII letters, digits, _ or -.
--peer <HTTPS-URL> Unset Required for a new owner; publishes its individual private control-plane address as a mandatory member. An existing owner's address must match.
--ttl-secs <SECONDS> 600 Permission lifetime, from 60 to 3600 seconds.

Expected: exit 0 and a nonempty file with mode 0600. Failed validation or store access produces an error. A membership addition takes effect before grant issuance; if issuance fails, the new member still participates in the rotation acknowledgement requirement. Inspect membership before retrying.

Transfer the file through a protected channel, make it readable by the joining Authority service and remove transfer copies. A different owner or nonce cannot reuse the permission after consumption. The same persisted request can recover a lost response while the permission remains valid. The permission's expiry or removal of its file does not revoke an Authority that has already bootstrapped. Complete Authority bootstrap, then remove the one-time input after readiness and restart checks pass.

gdsgate authority keys legacy migrate

Brings the keys named by the five [audit].*_key_path parameters into the two keyrings [security.integrity] names, as records that verify rows and never seal them.

gdsgate --config authority.toml authority keys legacy migrate

Nothing is re-sealed and no key is generated. Each key keeps its fingerprint; existing rows keep verifying. Until a root is added to a ring, new rows are sealed under the key files; keep the five parameters set.

The five parameters are deprecated. To retire them: run this command, add an active key to each ring with keys rotate <ring> --prepare and --activate, then remove the parameters from the configuration.

A second run adds nothing and changes no file.

Refused: a key whose ring is not named in the configuration; a ring given as ${env:...} or ${exec:...} (no file to write).

gdsgate authority ca seeds wrap

Puts every CA signing seed already in the store under the wrapping key [authority].ca_kms_key names.

gdsgate --config authority.toml authority ca seeds wrap
3 of 3 CA signing key(s) wrapped under `ca`.

With ca_kms_key set, new seeds are written wrapped; existing ones stay as written until this command runs. It can run against a serving cluster. Each row is converted separately; an interrupted run leaves a mix, which is read correctly (each row's seed column records its form). A second run changes nothing.

the store holds no CA signing keys yet, so there is nothing to wrap.

Refused when [authority].ca_kms_key names no key, or when a row is wrapped under a different key.

gdsgate authority ca keys prune [--delete]

Removes the private keys of CA generations nothing will use again.

gdsgate --config authority.toml authority ca keys prune
2 CA generation(s) can be removed:
  user-ssh generation 1 (window closed 412d ago)
  user-ssh generation 2 (window closed 380d ago)
Nothing was removed. Run this again with `--delete` to remove them; there is no
command that puts one back.

A rotation keeps the retired generation's private row in ca_private_key; only this command removes it. Under [authority].ca_kms_key each stored row costs a key-service call at every start.

A generation is listed when all hold: its validity window closed more than thirty days ago, no rotation role names it, it is not one of its authority's two newest generations, and the trust registry no longer publishes it.

Without --delete the command only lists. With it, each removal is audited as ca.key_pruned (CAS-0201) before the row is deleted, naming the authority, the generation and when its window closed. A removed key cannot be restored. A second run finds nothing to remove.

gdsgate authority keys status

Prints what each keyring holds: every key by fingerprint, what it may still be used for, and how many rows in the store still name it. No key material.

gdsgate --config authority.toml authority keys status
control  file /etc/gdsgate/control.keys
  active       3f9c1a2b4d5e6f708192a3b4c5d6e7f8  root, every domain derived from it
               4 rows name it: access_request 2, step_up_grant 2
               2 of 2 replicas hold it
  verify_only  a1b2c3d4e5f60718293a4b5c6d7e8f90  approval key, taken as it stands
               no row names it
  replicas: auth-1 confirmed 12s ago, auth-2 confirmed 47s ago
audit  file /etc/gdsgate/audit.keys
  active       d4e5f60718293a4b5c6d7e8f90a1b2c3  root, every domain derived from it
               812 rows name it: audit_anchor 812
               2 of 2 replicas hold it
  replicas: auth-1 confirmed 12s ago, auth-2 confirmed 47s ago

A key is either a root (derives every domain's working key) or a single domain's key taken as it stands (the form after keys migrate-legacy). The row count comes from the key_id column of audit_anchor, access_request, step_up_grant, revocation_receipt, owner_revocation, delegation_grant, backend_inventory and mcp_tool_pin. A key can be retired once the count is zero.

Each Authority instance confirms the fingerprints it holds once a minute. The replica line under a key appears once at least one replica has confirmed that ring; before that the output reads replicas: nothing published (no Authority has recorded the fingerprints it holds). A replica silent for ten minutes leaves the count and is shown on the replicas: line with its last confirmation; it returns when it confirms again. keys rotate --activate and a production start-up check against the same set.

Keys declared lost with keys accept-loss appear in a trailing declared lost: block: fingerprint, keyring, when recorded, who accepted it, row count.

gdsgate authority keys verify

Recomputes each key's fingerprint from its material and compares it with the recorded one, and checks that no fingerprint is in both keyrings. Reads the keyring files only; works with the store unreachable.

gdsgate --config authority.toml authority keys verify
control  file /etc/gdsgate/control.keys
  3f9c1a2b4d5e6f708192a3b4c5d6e7f8  root, every domain derived from it its own key
audit  file /etc/gdsgate/audit.keys
  d4e5f60718293a4b5c6d7e8f90a1b2c3  root, every domain derived from it its own key
every key is the key its fingerprint names.

Run it after editing a keyring file by hand. Material replaced under an unchanged fingerprint makes every row the original key sealed fail verification. verify exits non-zero and lists every failing record.

gdsgate authority keys manifest show

Prints what must be kept with a database backup. Does not open the store.

gdsgate --config authority.toml authority keys manifest show
control  file /etc/gdsgate/control.keys
  covered by: nothing (the source holds the material as it stands)
  3f9c1a2b4d5e6f708192a3b4c5d6e7f8  active  root, every domain derived from it, created at 1755000000
audit  file /etc/gdsgate/audit.keys
  covered by: nothing (the source holds the material as it stands)
  d4e5f60718293a4b5c6d7e8f90a1b2c3  active  root, every domain derived from it, created at 1755000000
a database restored without these keyrings cannot be verified: every sealed row names the key that sealed it, and a key nothing holds cannot answer for its rows.
a keyring in the same backup as the database protects nothing: whoever holds that backup holds the rows and the key that seals them, and can write rows that verify.
the public keys above are the exception: publish them with the dump. They check the chain's signed checkpoints and seal nothing, so whoever holds them can verify the journal and forge no part of it.

covered by: nothing means the keyring material is not wrapped. The three closing lines print on every run. See Admin guide → Keeping keyrings out of the database backup.

gdsgate authority keys loss accept --key-id <FINGERPRINT> --accepted-by <NAME>

Records that one key is lost. Rows it sealed are then read as unverifiable (previously: forged). The record is sealed by the ring's active key and written to the audit chain.

gdsgate --config authority.toml authority keys loss accept \
    --key-id 3f9c1a2b4d5e6f708192a3b4c5d6e7f8 --accepted-by anna.ivanova
Flag Default Purpose
--key-id <FINGERPRINT> required Lost fingerprint as rows carry it: 32 hex characters. One key per run; keys status prints the fingerprints.
--accepted-by <NAME> required Person accepting the loss. Recorded on the audit row as asserted, unverified.
Refused when What to do
[security].profile = "production" Change the profile with set-profile first.
A keyring still holds the fingerprint The key is not lost.
No row names the fingerprint Nothing to accept.
Rows of both keyrings name it Check the fingerprint.

What changes, by table:

Row After the loss is accepted
revocation_receipt (and the equivalent workload-identity row) Still reads as revoked. A receipt sealed under a key now declared lost is no longer read as tampering.
access_request, step_up_grant, delegation_grant Refused, as any unverifiable row is; the request, step-up, or delegation has to be made again.
backend_inventory, mcp_tool_pin Discarded, as any unverifiable row is; the connector republishes them.
audit_anchor Left in place. verify-audit reports each stretch of chain it cannot read and continues.

See Admin guide → When a keyring is lost, including the loss of a whole ring.

gdsgate authority keys rotate <control|audit> <--prepare|--activate|--retire>

Moves a keyring to a new key in three steps:

Step Does Refused when
--prepare Adds a new key as prepared; the active key stays. Then distribute the ring and restart every Authority (a ring is read at start-up).
--activate New rows are sealed under the prepared key. A confirming replica has not published that it holds the key.
--retire Removes the old key. A row still names it. Always refused for the audit ring.

--prepare --kind signing creates the Ed25519 key that signs audit checkpoints; --kind hmac (default) creates the sealing key. Each kind rotates separately; --activate replaces the active key of its own kind only. The control ring takes no signing key.

See Admin guide → Rotating a keyring.

gdsgate authority audit checkpoint-keys export

Prints the public keys that verify the audit chain's signed checkpoints, one line per signing key in the ring, in any state. Not secret; keep the output with the database dump.

gdsgate --config authority.toml authority audit checkpoint-keys export > checkpoint-keys.txt

With that file and a dump, verify-audit --checkpoint-keys verifies the journal without key material. See Admin guide → Verifying the journal without a keyring.

Deprecated key commands

Deprecated

The five init-* commands below and the [audit].*_key_path parameters they write for are deprecated. They still run, print a warning on stderr, and are hidden from --help.

A new deployment keeps these keys in the keyrings of [security.integrity], which authority init creates. A deployment with key files brings them into the keyrings with authority keys legacy migrate.

gdsgate authority init-anchor [--path <PATH>] [--if-missing] (deprecated)

Generates a 32-byte HMAC anchor key and writes it hex-encoded to --path, or to [audit].anchor_key_path. The file is mode 0o600 on Linux; on Windows its access list is the owner and SYSTEM. An existing file is never overwritten.

gdsgate --config authority.toml authority init-anchor --path /var/lib/gdsgate/authority/anchor.key

Set [audit].anchor_key_path to the path in Authority's configuration and back the file up. Without it, anchored events cannot be verified.

Flag Default Purpose
--path <PATH> [audit].anchor_key_path Where to write the key file.
--if-missing off Existing key file: reported, exit 3. Without it: refused, exit 1. Nothing is overwritten in either mode.
gdsgate --config authority.toml authority init-anchor \
    --path /var/lib/gdsgate/authority/anchor.key --if-missing
# HMAC anchor key already present at /var/lib/gdsgate/authority/anchor.key, nothing to do

The file is created exclusively. Of two runs racing on one path, the second reports "already present".

When [audit].anchor_key_path names a variable or command in secret-reference notation, the command refuses; create that key where it is stored, or pass --path to write a file.

The four commands below take the same two flags and behave the same way.

gdsgate authority init-revocation-key [--path <PATH>] [--if-missing] (deprecated)

Generates a 32-byte HMAC revocation-receipt key, as init-anchor does. Set [audit].revocation_key_path to it; revoke-node then writes a sealed receipt that a database rollback does not undo.

gdsgate --config authority.toml authority init-revocation-key \
    --path /var/lib/gdsgate/authority/revocation.key
gdsgate authority init-approval-key [--path <PATH>] [--if-missing] (deprecated)

Generates a 32-byte HMAC key for JIT access requests, as init-anchor does. Set [audit].approval_key_path to it; approved request rows are then sealed against direct database edits.

gdsgate --config authority.toml authority init-approval-key \
    --path /var/lib/gdsgate/authority/approvals.key
gdsgate authority init-inventory-key [--path <PATH>] [--if-missing] (deprecated)

Generates a 32-byte HMAC key for live-discovery inventory receipts, as init-anchor does. Set [audit].inventory_key_path to it; each published backend inventory then carries a receipt that detects a direct rewrite of the row. Needed only with live resource discovery.

gdsgate --config authority.toml authority init-inventory-key \
    --path /var/lib/gdsgate/authority/inventory.key
gdsgate authority init-delegation-key [--path <PATH>] [--if-missing] (deprecated)

Generates a 32-byte HMAC key that seals each delegation grant, as init-anchor does. Set [audit].delegation_key_path to it. Needed only with delegation; unset, gdsgate delegations create is refused.

gdsgate --config authority.toml authority init-delegation-key \
    --path /var/lib/gdsgate/authority/delegation.key

gdsgate authority audit verify [--checkpoint-keys <FILE>] [--keyring <REFERENCE>]

Opens the store directly, replays the hash chain, and checks every row in audit_anchor against audit_log with the anchor key (the audit keyring, or the deprecated [audit].anchor_key_path). Exits non-zero on any integrity failure. Run it from a workstation that holds the key, separate from the Authority host.

gdsgate --config authority.toml authority audit verify
# audit chain ok (chain + anchors + checkpoints): 12483 rows, 4127 anchors, 61 checkpoints

--checkpoint-keys <FILE> is for an external reviewer. With the file keys checkpoint-keys printed, the run replays the chain and checks each signed checkpoint by signature; no keyring or anchor key is needed. Without the flag, checkpoints are checked against the public keys of the audit keyring when this host names one.

gdsgate --config reviewer.toml authority audit verify --checkpoint-keys checkpoint-keys.txt
# audit chain ok (chain + checkpoints): 12483 rows, 0 anchors, 61 checkpoints

--keyring <REFERENCE> is for an operator holding the audit keyring. It takes the secret-reference notation of [security.integrity].audit_keyring, including a wrapped ${kms.<name>+file:PATH} declared in [[kms_keys]], and overrides that setting for this run. Anchors are checked against its keys (in place of [audit].anchor_key_path) and checkpoints against its public keys.

gdsgate --config reviewer.toml authority audit verify --keyring /media/usb/audit.keys

With both flags, --checkpoint-keys checks the checkpoints and --keyring checks the anchors. Only the audit ring is accepted; a control ring is refused.

After keys accept-loss, an anchor stretch sealed under the lost key is reported; it does not fail the run:

not verified: serials 4..=9 (6 anchors) were sealed under key 3f9c1a2b4d5e6f708192a3b4c5d6e7f8, declared lost at 1755000000

The summary line comes first; everything outside a reported stretch was verified.

gdsgate authority audit list [filters]

Prints audit chain rows, newest first.

Route Behavior
gateway, certificate Decided by Cedar viewAudit; the read is audited.
store Opens this host's database directly (needs store_url); no running Authority needed.
default store when store_url is set, else gateway.

The default output is seven columns (time in UTC, event class, action, code, actor, target, outcome) with further fields on indented branch lines. --json prints one object per row with millisecond time. An empty result prints the header and (none).

gdsgate --config authority.toml authority audit list --class admin-action --limit 3
# # time                class        action          code      actor(verified)      target                             outcome
# 2025-07-29 09:20:04   AdminAction  catalog.manage  CAT-0101  gdsgate-provisioner  Database::"db-orders_prod"         Success
#     |- operation=create resource_kind=postgres
#     `- claimed operator (asserted by the caller, unverified): anna.ivanova@provider.example
# 2025-07-29 09:19:50   AdminAction  revokeNode                gdsgate-admin        EnrolledNode::"gdsgate-connector"  Success
# 2025-07-29 09:00:01   CaOperation  ca.rotate       CAS-0101  gdsgate-admin        CertificateAuthority::"user-ssh"   Allow

The actor comes from a certificate or token. A person is printed by the display name Authority keeps for the subject (see display_name_claims); a subject without one is printed as stored. --json carries both.

CODE is a stable event identifier: SES-0101 is a session opening in every release. Codes are never reused or renamed. The cell is empty when the writer records no code and on rows sealed before codes existed.

--node takes the node id that list-nodes, import-node, revoke-node and list-offered use. Rows Authority sealed for its own events and rows sealed before the node column existed match nothing.

--work <name> selects rows about one agent: sessions opened to its faces and sessions it opened. When one agent calls another's face, the row is filed under the called agent; the caller is in actor. Matches across certificate renewals and restarts.

An Authority older than --code, --node or --work ignores the filter. The command then filters the returned rows locally and prints one warning per filter to stderr (also under --quiet):

--node was not applied by this Authority, which is older than the filter; 2 of the rows it returned did not match and were left out here

Column widths are computed per page, and each page has its own heading. --route store returns one page.

Branch lines under a row:

Branch line Present when
Detail as key=value pairs, one line The row has a detail with fields (a ClientAssertion row lists nine sandbox properties; a database row carries the statement).
Claimed operator, marked unverified A control plane named a person.
submitted by node: <id> The row carries a node.
on behalf of: <owner> The row was made on someone's behalf (display name where known).
work: <name> The row is about an agent-host work.

Branch lines are dimmed on a colour terminal. Output to a file or pipe, or with NO_COLOR set, has no escape sequences.

--json carries the detail unescaped as the chain holds it; truncated says whether the server cut it.

gdsgate authority audit list --limit 200 --json |
  jq -r 'select(.detail != "") | [.time_unix_ms, .action, .detail] | @tsv'

In the text listing a branch line is four spaces, `- or |-, then the field:

gdsgate authority audit list --limit 200 | sed -n 's/^    [|`]- //p'

Columns are space-padded, not tab-separated; cut -f does not read them.

ClientAssertion holds unverifiable statements a client made about its own machine, such as the sandbox a delegated run reports. The actor is verified; the assertion is not.

CaOperation holds CA rotations and their refusals only. Certificates a CA signs are recorded under the class of what was signed.

Flag Default Purpose
--class <CLASS> every class One of authentication, authorization, ca-operation, session-activity, admin-action, capability-grant, client-assertion, resource-integrity. Matches the class the record was sealed with; older records keep their original class. Use --code to match across both.
--actor <ID> any Exact match on the verified actor, or on a display name (matches every subject with that name).
--action <PREFIX> any Action prefix: enroll., session., policy.. Rows keep the spelling they were sealed with; --code matches across spellings.
--code <CODE> any Exact match on the event code, for example SES-0101. Rows without a code match nothing.
--target <ID> any Exact match on the target.
--node <ID> any Exact match on the enrolled node that delivered the row (list-nodes id). Rows without a node match nothing.
--since <WHEN> no floor Inclusive floor on event time: an interval back from now (90s, 30m, 2h, 7d) or a date (2026-09-21, midnight UTC).
--since-ms <MS> no floor The same floor as a Unix-millisecond instant. Conflicts with --since.
--limit <N> 50 Cap on rows. 0 prints the whole chain (--route store only). Over a network, pages are fetched until the cap is met.
--json off One JSON object per row.

Use --json for scripts:

gdsgate --config authority.toml authority audit list --json --limit 1
# {"serial":682,"time_unix_ms":1785974336000,"class":"AdminAction","action":"audit.view",
#  "code":"AUD-0101","actor_verified":"anna.ivanova@provider.example",
#  "target":"AuditLog::\"chain\"","outcome":"Allow","detail":"{\"page_size\":1}",
#  "truncated":false,"submitter":"gdsgate-authority"}

The object is the same on every route.

Field Value
class, outcome An unknown code is printed as class(200).
truncated true when the server cut the detail.
code, submitter_node Absent when the row has none.
submitter_node Node that delivered the row; submitter is its role.
actor_name, owner_name Display names of actor_verified and owner; not part of the chain.
work Agent-host work.

Hashes are not included. The asserted operator name has no filter; filter on --actor. This command does not verify the chain; use verify-audit.

gdsgate authority store migrate [--check]

Brings the store's schema up to date and exits. Opens store_url directly; no running Authority needed. A second run applies nothing.

gdsgate --config authority.toml authority store migrate
# applied 3 migration(s); store schema up to date

gdsgate --config authority.toml authority store migrate
# store schema up to date — nothing to apply
Flag Default Purpose
--check off Report only. Exits non-zero if migrations are pending. Run before a rollout.
gdsgate --config authority.toml authority store migrate --check
# store schema has 2 pending migration(s): m0036_owner_revocation, m0037_delegation_grant_root_owner

With store_auto_migrate = false, a service start does not migrate; run this command as a rollout step.

A store with migrations this binary does not know was migrated by a newer build. migrate (both modes), a service start and gdsgate doctor refuse it; run the newer build on that host.

gdsgate authority policy template

Prints the starting policy for a new cluster on stdout. Runs offline.

gdsgate authority policy template > /etc/gdsgate/policy.cedar
Principal Granted
Group::"gdsgate-admin" (CN of the admin mTLS certificate) The six provisioning actions: account lifecycle, CA rotation, the catalogue, join tokens, the node registry.
Group::"gdsgate-provisioner" (CN=gdsgate-provisioner) The catalogue, the registry, join tokens for three named roles.
Group::"admins" (from the identity provider) Listing, sessions, databases, MCP tools, approvals, recordings, policy editing, the catalogue and the registries, through the gateway.

The template also carries the break-glass rule every later push is checked against. A second team group, SSH -L / -R and onward certificates are commented examples.

SSH is granted as connect plus sshProgram (evaluated for every shell, exec and subsystem from epoch 1). Narrow it with conditions on the rule (context.program_kind, git fields); the template shows an example. sshUseClientKey is commented; grant it when a resource with onward_auth = "client_key" is declared.

Without an external provisioning system, delete the Group::"gdsgate-provisioner" block.

Rename admins to a group your identity provider emits, set [policy].path to the file and start Authority. While the store holds no policy version, the file is seeded as version 1. See Policy → The starting policy.

gdsgate authority policy validate [<FILE>] [-o <FILE>] [--rule-ids]

Strict-validates a Cedar policy against gdsgate's schema and exits non-zero on any error. Runs offline.

gdsgate --config operator-workstation.toml authority policy validate
gdsgate --config operator-workstation.toml authority policy validate ./new-policy.cedar

Without <FILE>, validates [policy].path from the config.

A policy using the vocabulary retired in v0.8.23 (sshConnect, dbConnect, tcpConnect, k8sAccess, k8sReadOnly, the entity type Server) is migrated by the cluster on push and on load. This command migrates a copy, reports policy OK after migration, exits zero, and lists every change and everything to review. --output <FILE> writes the migrated text.

A rule over TcpService::"<id>" for an MCP backend needs the catalog to migrate; it is left unchanged and named in the output. The cluster completes it on push.

Anything else invalid fails.

--rule-ids lists the rules under the names policy.decision records use in deciding. A rule's name is a digest of its text and does not change when other statements are added or removed:

policy OK: ./policy.cedar
  34 rules, as an audit row names them:
  02726249cc2326ce  policy0    permit(principal in Group::"gdsgate-break-glass", action == Action::"editPolicy", resource);
  48bc8dac906e102c  policy1    permit(principal in Group::"gdsgate-admin", action == Action::"enrollNode", resource);

The middle column is the policy0 the record carries in deciding_at. Run the listing against the policy version the record was decided under (on the chain under policy.edit). For a migrated document, the migrated rules are listed.

Flag Default Purpose
-o, --output <FILE> not set Write the migrated policy here. Written only when the document needed migrating.
--rule-ids off Also list every rule under the name its decisions are recorded with.

gdsgate authority policy pull [-o <FILE>] [--version <N>]

Fetches the active policy from the cluster and prints it to stdout, or writes it to --output <file>. Decided by Cedar editPolicy. When the store has no policy yet, prints nothing and logs no policy stored yet (Authority is on the deny-all bootstrap) on stderr.

gdsgate --config client.toml authority policy pull -o policy.cedar
gdsgate --config client.toml authority policy pull --version 8 -o policy-v8.cedar
Flag Default Purpose
-o, --output <FILE> not set Write the policy here. Without it, stdout.
--version <N> active Fetch this stored version, in any state history lists. A version the store does not hold: no policy version N is stored.

gdsgate authority policy history [--limit <N>] [--json]

Lists the stored policy versions, newest first: the newest 50 unless --limit says otherwise. Decided by Cedar editPolicy.

gdsgate --config client.toml authority policy history
#    version  state        author       time                  change
#    v11      pending 1/2  Anna Orlova  2026-09-28 09:12 UTC  v10 +0 −1 ~0
# *  v10      active       Ivan Petrov  2026-09-27 20:25 UTC  v9 +1 −0 ~1
#    v9       rejected     Anna Orlova  2026-09-27 18:02 UTC  v8 +3 −0 ~0
#    v8       superseded   Ivan Petrov  2026-09-20 11:40 UTC  v7 +0 −0 ~2
Column Meaning
(first) * on the active version.
state active, pending (with approvals collected and required), superseded (was active), rejected (a proposal dropped before it became active).
author The name the user directory holds for the person who pushed it; else their subject; for a file seed or a break-glass restore, the verified pusher.
time When the version was stored, UTC.
change The version it is measured against, then statements added, removed and changed. ? when either side does not read as statements or its statements are not the policies Cedar reads.

A version is measured against the active version it was proposed on, not against the number before it: a rejected or pending proposal in between never took effect.

When older versions exist past the limit, a last line says so: older versions not shown (--limit 50); --limit 0 lists every version. With --json that line goes to stderr.

Flag Default Purpose
--limit <N> 50 The newest N versions. 0 lists every version.
--json off One JSON object per version: version, active, state, author, author_name, proposer, created_at, base_version, added, removed, changed, approvals, approvals_required.

gdsgate authority policy diff [<A>] [<B>] [--file <PATH>] [--stat] [--json]

Shows what changed between two policy versions, one Cedar statement at a time. Decided by Cedar editPolicy.

Arguments Compared
none The version the active one was based on → the active version.
<A> A → the active version.
<A> <B> A → B.
--file <PATH> The active version → the file.
gdsgate --config client.toml authority policy diff 8 9
policy v8 → v9 · Ivan Petrov · 2026-09-27 20:25 UTC · 2 changes

[1] ~ permit  connect  Group::"admins" → Database::"pg-demo"                  line 120
      permit(principal in Group::"admins", action == Action::"connect",
             resource == Database::"pg-demo")
    - when { context.mfa_satisfied };
    + when { context.mfa_satisfied && context.src_ip.isInRange(ip("10.0.0.0/8")) };

[2] + permit  sshForwardAgent  Group::"acme-admins" → SshHost::"win-ssh"      line 390
      // Agent forwarding on the Windows test host only, to check that a session
      // binds one agent socket however many of its channels ask.
    + permit(principal in Group::"acme-admins", action == Action::"sshForwardAgent",
    +        resource == SshHost::"win-ssh");

A statement is one permit or forbid with its annotations and the comment lines directly above it; a blank line detaches a comment. + added, - removed, ~ changed, with a line diff inside the statement. line is where the statement starts in the newer side (the older side for -).

Statements are paired in three passes, in document order:

  1. The same @id annotation.
  2. The same text. Layout and comments are not compared.
  3. The same effect, principal, action and resource scope. Two different @id values never pair.

A pair that differs in text or in its comment lines is ~. Moving a statement is not a change. Changes are numbered in the order of the newer side; a removed statement stands where it stood. [n] is the same for the same two versions every time; it is what revert --only takes.

Flag Default Purpose
--file <PATH> not set Compare the active version with this file. Conflicts with <A> and <B>.
--stat off Print the header lines only.
--json off from, to, and changes: number, kind, effect, action, principal, resource, id, line, matched_by, lines (op, text).

Colour is used only on a terminal.

Control characters in policy text and author names are printed escaped, as \u{1b}: C0 but TAB, DEL, C1 (U+0080–U+009F) and the bidirectional embeddings, overrides and isolates (U+202A–U+202E, U+2066–U+2069). TAB is printed as four spaces. The same applies to history, revert, review and pending-edits. --json escapes by JSON rules. The stored text is not changed; policy pull prints it as stored.

Refused Condition
no policy version N is stored A or B is not in history.
no policy is active yet No argument, one argument or --file, on a store with no policy.
does not read as policy statements One side has text outside a statement; the message names the line.
the statement view does not match what Cedar parses; review the raw text The statements read from one side are not the policies Cedar reads from it. Read the side with policy pull --version N.
the document does not parse as Cedar One side is not valid Cedar.

A // comment ends at a line feed or a carriage return, as in Cedar. Every side is also parsed by Cedar; the statements must be the same policies, in the same order, with the same text and effect.

gdsgate authority policy revert <N> [--only <LIST>] [--dry-run | --approve-plan <DIGEST>]

Undoes the changes version N made, on top of the active version, and publishes the result as a new version. The active version is edited statement by statement; an older version is never made active again. Decided by Cedar editPolicy.

gdsgate --config client.toml authority policy revert 9 --only 2 --dry-run
# reverting change [2] of v8 → v9 on top of active v10
#   - permit  sshForwardAgent  Group::"acme-admins" → SshHost::"win-ssh"
# schema check: ok
# would publish v11 · plan digest 5c1e0b7a90d4e213

gdsgate --config client.toml authority policy revert 9 --only 2 --approve-plan 5c1e0b7a90d4e213

The changes are those of policy diff <base> <N>, <base> being the version N was based on (the header names it). For each undone change, the statement N left must be in the active version as N left it, comments included.

Without --approve-plan, the command prints the plan, publishes nothing, and prints the command that publishes it. The digest covers the active version, the changes chosen and the resulting text; any of them changing gives a different digest.

The result is pushed as policy push pushes: validated by the server, anchored to the active version the plan was computed on, and a pending proposal when [policy.editor].min_approvers is 2 or more. The policy.edit audit row carries reverted_from and, with --only, only.

Flag Default Purpose
--only <LIST> all Comma-separated change numbers from policy diff <base> <N>, or @id values.
--dry-run off Print the plan and publish nothing.
--approve-plan <DIGEST> not set Publish, if the plan's digest is this one.
Refused Condition
vN is pending / vN is rejected The version never took effect.
refused: the active version no longer holds what the reverted version left A statement to undo was changed or removed since N; each is listed with its number. Nothing is published.
schema check: failed The result does not validate against today's schema, usually an old statement using a retired action. The validator's error follows.
there is no change [n] / no change carries @id(...) --only names a change N did not make.
this plan is <digest>, not <digest> --approve-plan names a different plan. Run --dry-run again.
base version stale Another version became active after the plan was computed.

gdsgate authority policy review <N> | --pending <N> | --file <PATH>

A terminal screen for accepting or rejecting changes one at a time. Needs an interactive TTY. Reading the versions is decided by Cedar editPolicy.

Arguments Changes reviewed
<N> What N changed against the version it was based on, as policy diff <base> <N> numbers them. N is active, superseded or rejected.
--pending <N> What the pending proposal N changes against the version it was proposed on.
--file <PATH> The active version → the file.
gdsgate --config client.toml authority policy review 9
┌ policy review · v8 → v9 · proposed by Ivan Petrov ─────────────────────────────────────┐
│ Changes                     │ [1] ~ permit  connect                                    │
│▸ [1] ✓ ~ connect pg-demo    │     Group::"admins" → Database::"pg-demo"                │
│  [2] ✗ + sshForwardAgent ...│     in effect in v10 · r undoes it                       │
│                             │                                                          │
│                             │   permit(principal in Group::"admins", action ==         │
│                             │ Action::"connect",                                       │
│                             │          resource == Database::"pg-demo")                │
│                             │ - when { context.mfa_satisfied };                        │
│                             │ + when { context.mfa_satisfied &&                        │
│                             │ context.src_ip.isInRange(ip("10.0.0.0/8")) };            │
│────────────────────────────────────────────────────────────────────────────────────────│
│ accepted 1 · rejected 1 · open 0      schema: ok      result: 1 change against v10     │
│ a accept  r reject  u undecide  ↑↓ move  d full diff  Enter publish as v11  q quit         │
└────────────────────────────────────────────────────────────────────────────────────────┘

The result is computed on the active version, whatever the source:

Mark The active version carries the change It does not
✓ accepted Kept. Applied.
✗ rejected Undone. Left out.
blank, open Kept. Left out.

A change the active version carries starts ✓; any other starts open. So review N of the version in effect starts with nothing to publish, and rejecting changes there is revert --only chosen by hand. review --file starts open; accepting takes the file's changes over one at a time.

An applied change follows the rules of an undo in reverse. An added statement goes in after the nearest statement above it that the active version holds. A statement to change or remove must be in the active version as the old side had it, comments included. Otherwise the bottom line reads result: refused: [n] changed since and nothing can be published.

The bottom line counts the marks, gives the strict schema check of the result, and what the result changes against the active version.

Enter shows the plan: what the result changes against the active version, the schema check, and the version it would become. y publishes it the way policy push does: validated by the server, anchored to the active version the screen was computed on, and a pending proposal when [policy.editor].min_approvers is 2 or more. Any other key goes back. A failed schema check shows the validator's error and no plan.

The policy.edit audit row of a publish carries:

Key When
reverted_from, only review N undid changes of N. only lists them when not all were.
based_on review N applied changes of N, or any publish from --pending N.
A pending proposal

With --pending N the key line reads A approve as proposed R reject Enter publish my selection as a new proposal.

Key Action
A, then y One vote on N as proposed, as approve-edit N casts it. The marks play no part.
R, then y Rejects N as a whole, as reject-edit N does.
Enter, then y Publishes the marked result as a new proposal of yours, with based_on = N.

A selection is never a vote on N, and N stays pending. The new proposal needs its own approvals, and its author cannot approve it.

Key Action
a / r / u Accept / reject / clear the mark on the selected change.
↑ ↓ or k j Select a change.
d Show every change, or the selected one again.
PgUp / PgDn Scroll the right pane.
Enter Show the plan.
q / Esc Quit, publishing nothing.
Refused Condition
policy review needs a terminal Standard input or output is not a terminal. Use policy diff and policy revert --only.
vN is pending approval review N of a pending proposal. Use --pending N.
vN is <state>, not a pending proposal --pending N of a version that is not pending.
vN became active after this review was computed Another version became active before the publish. Nothing was published; run the review again.
base version stale The same race, caught by the server.
nothing to publish The marks give the active version back.
does not read as policy statements A side does not read as statements, is not valid Cedar, or its statements are not the policies Cedar reads. The screen does not open, so A is not offered; read the proposal with policy pull --version N.

gdsgate authority policy push <FILE>

Strict-validates <FILE> locally, then uploads it as the new active policy. The server validates again, stores a new version, and reloads it on every Authority instance without restart. Decided by Cedar editPolicy.

gdsgate --config client.toml authority policy push ./policy.cedar

Policy lock-out risk

The policy you push must keep an editPolicy permit for your group, or no one can push again. Recover on the Authority host through the emergency administrative socket if configured (policy push --route emergency, no restart), or through [policy.editor].break_glass_policy_file, applied at restart past the editPolicy gate. The [policy] file does not recover a lock-out: it seeds only a store with no policy version. See Admin guide → Lock-out and recovery.

gdsgate authority policy edit

Opens the active policy in a full-screen terminal editor. The policy is pulled on entry; Ctrl-S validates it locally and pushes it. Both calls are decided by Cedar editPolicy. Needs an interactive TTY.

gdsgate --config client.toml authority policy edit

Every keystroke runs the policy validate checks. The status bar shows the statement under the cursor, the number of lines changed since the pull, the lint state (✓ or ✗ with the first error), a migration notice, the keybindings and the last deploy result; the failing line is highlighted red. A document in the vocabulary retired in v0.8.23 is not an error: the status bar lists what the migration changes, and Ctrl-S migrates it as push does.

Tab completion depends on the cursor position:

Position Candidates
Inside a Type::"..." literal Ids of that type from the schema and live catalog (Action::"<action>", SshHost::"<id>", Database::"<id>").
After resource. / principal. / context. Attributes declared for that base; resource also offers getTag / hasTag.
Elsewhere Entity types and actions from the schema, Cedar keywords, resource ids from gdsgate resources list.

Typing narrows the popup, Backspace widens it, Enter or Tab accepts, any other key cancels.

Ctrl-L runs cedar-policy-formatter; Ctrl-Z right after undoes the formatting in one step. Copy and paste use an internal buffer; the system clipboard is not used. On a successful Ctrl-S the editor exits and prints policy deployed with the version now active. Ctrl-Q on a modified buffer asks for confirmation; Y discards.

Key Action
Type / arrows Edit and move.
Home / End Start / end of the line.
PgUp / PgDn One screen up / down.
Ctrl-Home / Ctrl-End Start / end of the document.
Shift + any of the above Extend selection.
Tab Open context-aware completion; four spaces where nothing completes.
Ctrl-A Select all.
Ctrl-C / Ctrl-X / Ctrl-V Copy / cut / paste (internal).
Ctrl-Z / Ctrl-R Undo / redo.
Ctrl-F Search; Enter for the next match, Esc to exit.
Ctrl-L Format the buffer.
Ctrl-O / F2 Move the keys to the statement tree and back.
Ctrl-G Go to a statement by number.
Ctrl-S Validate and deploy (push, then hot-reload).
Ctrl-Q Quit; confirms when dirty.
Statement tree

The column on the left lists the statements of the buffer. #n is the statement's position in the current text, counted from 1 and recounted on every change; an @id is shown beside it. These numbers are not the [n] of policy diff, which numbers changes between two versions.

Groups: by action (a statement with action in [...] is listed under each of its actions; no action constraint → any action), by principal, by resource, or file order. forbid statements form a group of their own. ✗ marks a statement holding a validation error, and a closed group containing one.

While the cursor is in the text, the tree marks the statement under it with ›. While the text does not split into statements (a statement half typed, an unbalanced parenthesis), the tree keeps the last good split and its title says stale. A whole statement with a Cedar error stays in the tree, marked ✗.

In the text, a control character takes one cell: ␛ for ESC, ␍ for CR, ␀–␟ for the rest of C0, ␡ for DEL, a space for TAB, � for C1 and bidirectional controls. The tree and the status bar show them as \u{1b}. The buffer keeps the characters as they are.

Below 90 columns the tree is hidden; Ctrl-O draws it over the text.

Key (tree focused) Action
↑ / ↓ Move. The text scrolls to the selected statement and marks it; the cursor stays.
→ / ← Open / close a group; ← on a statement goes to its group.
Enter Cursor to the statement's first line; keys back to the text. On a group: open / close.
/ Filter by text; Enter keeps the filter, Esc clears it.
v Next grouping.
Esc, Ctrl-O, F2 Back to the text; cursor and scroll as before.
Other Ctrl keys Back to the text, then the key acts there.

Tab never leaves the text. Ctrl-G opens a go to # field: the text scrolls to the statement as the number is typed, Enter moves the cursor there, Esc returns to where it was. A number past the last statement is reported in the field. The mouse is not used.

gdsgate authority policy proposals list

Lists policy proposals waiting for approval. With [policy.editor].min_approvers of 2 or more, a push becomes a pending proposal. Decided by Cedar approvePolicyEdit.

gdsgate authority policy proposals approve <version>

Casts one approval on a pending proposal (<version> from pending-edits). At the configured number of distinct approvers the proposal becomes the active policy on every Authority instance. Decided by Cedar approvePolicyEdit.

gdsgate --config client.toml authority policy proposals approve 7

gdsgate authority policy proposals reject <version>

Rejects a pending proposal as a whole. It moves to rejected in history and no later vote activates it. Rejecting a proposal already rejected succeeds and changes nothing. Decided by Cedar approvePolicyEdit, as a vote is; the proposer may reject their own proposal if the policy lets them vote. Audited as policy.edit_approve with operation = reject.

gdsgate --config client.toml authority policy proposals reject 7
Refused Condition
no such policy proposal No version <version> is stored.
proposal is not PendingApproval The version is active or superseded.

gdsgate authority recordings list [filters]

Lists recent session-close events, newest first. Decided by Cedar viewRecordings.

gdsgate --config client.toml authority recordings list --target prod-db --limit 20
# time                 kind  session                               actor  owner  target   outcome  bytes
# 2026-09-20 14:02:11  ssh   3f7c1c2e-9a11-4a54-8b0e-7c0a9d2e4b31  alice  -      prod-db  ok       48.2K
Flag Default Purpose
--actor <name> not set Filter by the certificate subject that ran the session.
--target <id> not set Filter by resource id.
--since <when> not set Only events at or after this time: an interval back from now (90s, 30m, 2h, 7d) or a date (2026-09-21, midnight UTC).
--since-ms <unix-ms> 0 The same floor as a Unix-millisecond instant. Conflicts with --since.
--limit <n> 50 Cap the number of rows; 0 uses the server default.
--wide off Add the uri and stored columns.
--json off One JSON object per row; the default is the table.

Columns: time (UTC), kind, session, actor, owner, target, outcome, bytes. session is the full UUID that recordings show takes. owner is the delegation owner of a session run on someone's behalf, else -.

--wide adds uri (the cast URI from the close event, recorded before any upload) and stored:

stored Meaning
central <time> The recording store holds the bytes since <time>.
connector The session recorded data the store does not have.
- No recording.

gdsgate authority recordings show <session_uid>

Prints a finished recording to stdout. A terminal recording is printed as cast bytes; pipe it into asciinema play -. A web or model exchange is rendered as requests, responses and what was not recorded; see Configuration → Recording the exchange. --raw prints the stored bytes unchanged. Decided by viewRecordings.

SESSION_UID="<session_uid>"
gdsgate --config client.toml authority recordings show "$SESSION_UID" | asciinema play -
gdsgate --config client.toml authority recordings show "$SESSION_UID"          # a web or model exchange, rendered
gdsgate --config client.toml authority recordings show "$SESSION_UID" --raw    # the artifact's own bytes

gdsgate authority recordings live <session_uid>

Tails an in-progress recording, printing asciicast chunks as they arrive. <session_uid> comes from gdsgate sessions list. Interrupt to stop. Decided by viewRecordings.

create-token rotate-ca rotate-onward-ca rotate-host-tls-ca rotate-spiffe-ca rotate-jwt-ca onward-ca-pub onward-ca-pubs policy recordings audit list-nodes list-offered revoke-node delete-node forget-user list-workloads revoke-workload import-node list-serving list-resources set-resource list-scopes set-scope remove-scope list-agents set-agent switch-agent remove-agent remove-resource activate bootstrap-admin bootstrap-provisioner init set-profile init-anchor init-approval-key init-revocation-key init-delegation-key init-inventory-key keys verify-audit migrate

list show live

list

validate pull push edit approve-edit reject-edit pending-edits history diff revert review template

Connector enrollment publication

authority publication show prints the shared mode, revision and cluster ID. authority publication set auto|manual changes the default for new Connector identities. Existing nodes retain their assigned mode, namespace and limits. Both commands require Cedar manageCatalog and support the certificate and gateway routes, plus --json.

gdsgate authority publication show --json
gdsgate authority publication set auto

The setter reads the current revision and applies a compare-and-swap. A concurrent change refuses the write. Use --if-revision N to supply a known revision in automation. The policy and its required audit evidence commit together.

Manual resource assignments

A manual resource can be served by several Connector nodes. Declare the catalog resource first, then assign its serving nodes by their Registry UUIDs from gdsgate authority nodes list:

gdsgate authority resources members shared-ssh --json
gdsgate authority resources bind shared-ssh --node "$CONNECTOR_ID"
gdsgate authority resources bind shared-ssh --node "$OTHER_CONNECTOR_ID" --local-id local-ssh
gdsgate authority resources unbind shared-ssh --node "$CONNECTOR_ID"

The backend ID defaults to the catalog ID. Use --local-id when the Connector's configuration declares a different ID. Legacy manual Connector identities require the same local and catalog ID. The existing catalog kind, port and SSH onward mode remain the contract for every assigned node.

members returns the membership revision and current node_id/local_id pairs. Revision 0 means legacy ownership. A positive revision with no members closes serving until another explicit assignment. Unbinding the last node keeps that closure. Automatic resources cannot become manual shared resources.

Both setters read the current revision, then make one compare-and-swap attempt. Use --if-revision N for automation, including 0 before the first assignment. A concurrent change returns an error; read members before retrying. An unchanged repeat keeps the revision and records its audit event. All three commands support --json and the gateway, certificate and emergency routes.

Catalog-wide manageCatalog is required before membership lookup. Writes commit assignments and required audit evidence together. Failure of the audit write leaves the existing membership intact. Connector publication uses the accepted mappings; withdrawal invalidates old routes and the resource's inventory observation.