Discovery 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 [discovery] or the nested array-table
heading. Each row gives the serde type and source default or unset meaning.
Check parsing, unknown keys, and catalog cross-field rules with
gdsgate doctor --config <PATH> --json, then follow the resource catalog
lifecycle.
[discovery]¶
Used by: Authority.
The resource catalog Authority seeds into the store on startup. It powers
RBAC-filtered listing (gdsgate ls), attribute and label policies on the Cedar
side (live resource metadata is read on every authorisation), and the
per-resource JIT approval threshold cascade.
The catalog is not required for the data plane to work: connectors declare what
they serve at registration and the Proxy routes by that. It is required for any
policy that reads resource.environment or resource.getTag("team").
[discovery] is one of two ways into the catalog; the other is Authority's
catalog API, which changes it against a running cluster. Seeding is an upsert:
every Authority start writes what the file declares over the stored rows and
removes nothing, so a cluster whose catalog is managed through the API keeps
[discovery] empty. See
Admin guide → Resource catalog lifecycle.
[[discovery.resources]]: declared resources¶
| Key | Type | Default | Purpose |
|---|---|---|---|
id |
string | unset, required | Resource id. Matches what the connector registers and what clients request. |
kind |
string | unset, required | One of ssh, postgres, mysql, kubernetes, tcp, mcp, llm, web, web-egress. Selects the Cedar entity type (SshHost, Database, KubernetesCluster, TcpService, McpServer, LlmService, WebApp, WebEgress). The catalog has no cockroach resource kind. |
project |
string | "default" |
Owning project (Cedar parent: <entity> in Project::"<project>"). Use it to partition by team or tenant. |
min_approvers |
usize | unset | Resource-local JIT threshold. Written into the resource's catalog row at every start, which is where the approvals cascade reads its narrowest level from. |
hostname |
string | the resource id, when it is a legal name | Name clients may address the resource by, carried back in the listing. One DNS label: letters, digits and -, case-insensitive, up to 63 bytes, no - at either end. An id carrying _ or . is not a legal name, so a resource with such an id has none until hostname is set. |
aliases |
array of string | [] |
Further names for the same resource, same rules as hostname. |
port |
u16 | the port of the protocol: ssh 22, postgres 5432, mysql 3306, kubernetes 6443, web 80 |
Port clients connect on. tcp, mcp, llm and web-egress have no well-known port, so those kinds must declare one. web's 80 is the port a browser handed only a name dials on its own, since this hop carries no TLS, and a declared port wins over it. Independent of the connector-side [[connector.backends]] addr, which is never published. |
native_name |
string | unset | The domain the application itself answers on (app.example.com): a whole name, at least two labels, that gdsgate does not own. web only, and refused on any other kind. Declaring it publishes nothing on its own; it is the fact a resident client needs before it can offer to answer to that name locally, and it answers only where the person running that client has also named it in their own [client].intercept_names. It is a different field from hostname, which is a single label this cluster hands out and resolves inside a client's zone; this is a name somebody else owns, the same authority the connector puts in Host when it forwards ([[connector.backends]] url). See Resident client → An application's own name. |
pin_tool_descriptors |
bool | false |
mcp only. Pins the server's tool descriptors: the first descriptor seen for a tool becomes its baseline, and a tools/call on a tool whose descriptor has changed since is refused until the change is re-approved. Declared here, on the resource, rather than on the connector's backend entry; see Pinning tool descriptors. Set on any other kind, it refuses the start. |
The connector backend vocabulary is wider than the catalog wire vocabulary in one
deliberate case. A [[connector.backends]] entry may use kind = "cockroach"
and dial CockroachDB on port 26257; the catalog has no Cockroach resource
variant. Publish that backend as a [[discovery.resources]] row with
kind = "postgres" and port = 26257. The Authority kind_to_proto and
resource_kind_to_str mappings accept the nine catalog kinds above and map no
Cockroach value.
Names are unique across the catalog, native_name included: two resources
claiming the same name, declared or defaulted from an id, is refused at startup,
as is a tcp, mcp, llm or web-egress resource without a port.
[[discovery.import_rules]]: label and environment rules¶
Applied in order to every declared resource whose id matches the glob.
Environment defaults to dev; the last matching rule wins. Labels merge across
matches, with later winning per key.
| Key | Type | Default | Purpose |
|---|---|---|---|
match |
string | unset, required | Glob over resource id. Only * is special and it matches any run, including empty. |
environment |
optional string | unset, resolved environment dev |
Environment to set on a match. |
labels |
table of string to string | {} |
Labels merged on a match and exposed to Cedar as resource tags. |
Reserved labels¶
A few label keys are read by gdsgate itself as well as by the Cedar policy.
They behave like any other label, merged across matching rules and available to
Cedar through resource.getTag("environment"), and a specific value also flips a runtime
behaviour.
| Key | Value | Effect |
|---|---|---|
pin_src_ip |
"true" |
Every access certificate issued for this resource is bound to the client's TCP peer address at issue time. The connector rejects a session whose actual peer does not match. Any other value, or absence, leaves the certificate un-pinned and usable from any peer. |
A full discovery example¶
[[discovery.resources]]
id = "web-01"
kind = "ssh"
project = "frontend"
[[discovery.resources]]
id = "web-02_prod"
kind = "ssh"
project = "frontend"
min_approvers = 2
[[discovery.resources]]
id = "db-orders_prod"
kind = "postgres"
project = "backend"
min_approvers = 3
hostname = "orders-db" # the id itself is not a legal name (`_`)
aliases = ["orders"]
[[discovery.resources]]
id = "redis-cache_prod"
kind = "tcp"
port = 6379 # required: tcp has no well-known port
[[discovery.resources]]
id = "tools-mcp"
kind = "mcp"
port = 9000
# Glob rules:
[[discovery.import_rules]]
match = "*_prod"
environment = "prod"
labels = { criticality = "high", on_call = "primary" }
[[discovery.import_rules]]
match = "*_staging"
environment = "staging"
labels = { criticality = "medium" }
[[discovery.import_rules]]
match = "web-*"
labels = { team = "frontend", tier = "edge" }
[[discovery.import_rules]]
match = "db-*"
labels = { team = "backend", owner_squad = "orders" }
# Source-IP pinning for the strictest backends: the issued cert is
# bound to the caller's TCP peer; the connector rejects re-use from any
# other peer.
[[discovery.import_rules]]
match = "*_prod"
labels = { pin_src_ip = "true" }
The catalog is seeded once at Authority startup; changes need an Authority restart, since there is no hot reload in v1.