Skip to content

Endpoints configuration

The flat configuration hub remains the compatibility route. This page is the canonical source-checked field reference for its family, including nested tables and list rows.

Paths in the tables are relative to [endpoints]. Each row gives the serde type and source default or unset meaning, and says whether the value is a bind or dial address. Check parsing and endpoint cross-field rules with gdsgate doctor --config <PATH> --json, then follow the multi-node cluster guide.

[endpoints]

Used by: Authority, Proxy, Connector, Client. Each reads only the fields it needs: Client only proxy_public, Authority only its own listeners.

Bind targets (for the service that owns the listener) and dial targets (for peers that consume it), as host:port. Bind 0.0.0.0 in containers; dial by service name or DNS.

Key Type Default Purpose
authority optional string 127.0.0.1:50051 when unset Authority gRPC control plane (mutual TLS once the cluster is registered). The Proxy dials this; a connector dials it for two-layer SSH-forward decisions and for downstream-cert issuance.
authority_failover array of strings [] Additional Authority endpoints for HA. The Proxy's connection pool follows the audit write-leader across these.
authority_enroll string 127.0.0.1:50050 Authority's plaintext bootstrap listener. New nodes register here with a one-time token.
proxy_public string 127.0.0.1:50061 Public, client-facing Proxy listener.
proxy_internal string 127.0.0.1:50062 Connector-facing reverse-tunnel listener.
proxy_ws string 127.0.0.1:50063 Connector-facing WebSocket fallback listener.
proxy_join string empty, listener off Proxy listener that relays node registration and token-less renewal to Authority.
[endpoints]
authority          = "auth:50051"
authority_failover = ["auth-2:50051", "auth-3:50051"]
authority_enroll   = "auth:50050"
proxy_public  = "0.0.0.0:50061"
proxy_internal = "0.0.0.0:50062"
proxy_ws      = "0.0.0.0:50063"
proxy_join    = "0.0.0.0:50064"

authority may be omitted: unset, it is 127.0.0.1:50051 wherever it is bound or dialled. Omitting it matters on a connector running authority_transport = "fronted", which then derives the Authority control plane from proxy_internal and needs only one address.

proxy_join: the join relay

A node obtains its transport identity before it has a reverse tunnel, so neither registration nor renewal can travel one. A connector that must never address Authority (authority_transport = "tunneled") therefore needs a listener on the Proxy that stands in for both Authority listeners. proxy_join is that address, both the bind address on the Proxy and the dial address on the node.

It is one of two ways to serve that. The other is the gateway's own client-facing port ([proxy].public_join), which spares a node a second address entirely. proxy_join is the one to use where the join belongs on an address of its own; the two can run side by side.

The relay reads the first bytes of each connection and routes on their shape: a TLS handshake goes to authority (renewal), the HTTP/2 cleartext preface goes to authority_enroll (first registration), anything else is refused and the connection closed. TLS is not terminated, so a renewal presents the node's own certificate to Authority and Authority's checks work as on a direct dial.

Join relay HA behavior

Both destinations are the primary ones, authority and authority_enroll, never an authority_failover address. Registration and renewal both write the audit chain, so only the write-leader serves them; while the leader is some other instance, a join or a token-less renewal through the Proxy is refused with "unavailable".

Nothing else breaks: nodes already holding a valid identity keep working, and the tunnelled control plane (below) does follow the leader. A node retries its renewal, starting an hour before expiry against a 24-hour leaf, and each attempt is bounded, so the retries keep coming for as long as the leader is elsewhere. It does not fall back to a registration token: that path exists only at start-up, and only while the token is unspent. A first registration is an operator action and can be repeated, or aimed straight at Authority.

Where a deployment cannot live with that, register nodes against Authority's own addresses instead of through the relay.

Join relay authentication

Every other Proxy listener demands mutual TLS or a token; a node joining has neither yet. Expose the address deliberately. What protects it:

  • The destination is not the client's to choose. Both addresses come from the Proxy's own configuration. Nothing on the wire can name, redirect, or add one: the first bytes select a branch, not a host.
  • A per-source-IP accept rate limit, 5/s with a burst of 10, the same budget Authority applies to direct registrations. It has to live here, because relayed joins all reach Authority from the Proxy's address, so Authority's own limit could not tell one flooding node from a fleet.
  • A ceiling of 64 concurrent joins, a 5-second limit on how long a connection may stay silent, and a 30-second idle limit on an established relay. A join is a short exchange, and none of these bite a real one.

Those five figures are what the relay runs on when nothing says otherwise, and a site whose rollout is larger than they allow states its own; see the join_* keys in [proxy]. Unlike every other limit there, leaving a key out keeps the figure above, and a stated 0 means unlimited.

[proxy].single_port does not multiplex this listener; it stays on its own address.

Join relay TLS behavior

It terminates neither side, because a renewal's mutual TLS has to reach Authority intact. Whether a registration crossing it is encrypted is decided at the far end, by Authority's registration listener; see [authority]. With no certificate there, every provision token relayed through this address travels in cleartext. gdsgate doctor reports the relay, the limits in force, and which of the two cases this deployment is in.

What Authority tells a joining node

Authority answers a registration, and every token-less renewal after it, with the node's identity and with the settings that identity is used alongside:

Advertised From
the reverse-tunnel address a connector serves through proxy_internal
the WebSocket fallback address proxy_ws
the route to Authority this cluster runs (direct / fronted / tunneled) [connector].authority_transport on Authority's file
Authority's control-plane address, on the two routes that dial it authority
where to renew, as a URL with its scheme authority, proxy_join, or proxy_public, by route; see below
the name this cluster answers to on a shared front, where it stands behind one [transport].front_sni

Nothing here is a secret, and nothing here is anonymous either: it rides a grant, so its reader has already spent a one-time provision token. It does not replace [enroll] or [endpoints] on the node, which still runs the file it was started with, and it is what a single installation command reads instead of deriving addresses from a domain by convention.

Two rules make it worth depending on:

  • A bind target is not an advertised address. Every key in this section is a bind address, and a container deployment writes 0.0.0.0 in all of them. A wildcard bind is advertised as nothing at all, because a node handed 0.0.0.0:50062 would have no other address for its own gateway; told nothing, it says so.
  • The scheme is Authority's to name. Whether a renewal runs mutual TLS is decided by the URL scheme and nothing else, so the renewal address is advertised with the scheme this cluster speaks; the node does not assemble it. A node that is handed one it cannot use refuses and quotes what arrived.

On the tunnelled route the renewal address is proxy_join where a relay is bound, and the gateway's own client-facing address where the Proxy carries the join there instead ([proxy].public_join): single_port when the deployment multiplexes onto one port, proxy_public otherwise. A relay of its own wins where both are configured. On direct and fronted it is authority.

Both of those are Proxy settings that Authority has to know, for the same reason it has to know [connector].authority_transport: Authority is what tells the fleet. In a split deployment, public_join = true goes in Authority's file as well as in the Proxy's.

Authority says at start-up what it will advertise, and warns when its own [endpoints] name nothing worth handing out.

Split deployments must declare the Proxy's addresses

Authority advertises the [endpoints] it holds. In a deployment where Authority and the Proxy run from separate files, the Proxy's addresses have to appear in Authority's file too: they are dial targets there, not bind targets, and Authority binds nothing from them. Without them a joining node is told only its identity.