Open-Source Wikis

/

Consul

/

Systems

/

Proxy config (proxycfg)

hashicorp/consul

Proxy config (proxycfg)

agent/proxycfg/ is the bridge between Consul's catalog/config-entry world and the xDS server. For each Connect proxy registered locally, it maintains a ConfigSnapshot containing everything Envoy needs.

Purpose

A Connect proxy needs much more than a list of upstream addresses. It needs:

  • The compiled discovery chain (resolver + router + splitter) for each upstream.
  • The current root CA(s) and a fresh leaf certificate.
  • The set of intentions that apply to the proxy's service.
  • JWT providers referenced by api-gateway routes.
  • Mesh config (per-cluster TLS settings, transparent-proxy mode, ...).
  • Federation state for cross-DC mesh gateways.
  • Peering trust bundles when peer-to-peer mesh is in use.

Building this on every xDS poll would crush servers. The proxy config manager subscribes to all of these inputs once, assembles a snapshot, and pushes new versions to xDS as inputs change.

Directory layout

agent/proxycfg/
├── manager.go                # The Manager that owns one watcher goroutine per proxy
├── proxycfg.go               # Top-level types and constants
├── snapshot.go               # ConfigSnapshot and per-kind sub-snapshots (46 KB)
├── state.go                  # The state machine for one proxy
├── connect_proxy.go          # connect-proxy specific population
├── mesh_gateway.go           # mesh-gateway specific population (31 KB)
├── ingress_gateway.go        # ingress-gateway specific population
├── terminating_gateway.go    # terminating-gateway specific population
├── api_gateway.go            # api-gateway specific population
├── upstreams.go              # Upstream resolution helpers
├── data_sources.go           # The DataSources interface and aggregations
├── naming.go                 # Cluster/SNI/listener naming helpers
├── config_snapshot_glue.go   # Glue between proxycfg and other agent components
└── proxycfg.deepcopy.go      # Generated deep-copy methods (36 KB)
agent/proxycfg-glue/         # Adapts agent caches to the DataSources interface
agent/proxycfg-sources/      # Boot-time wiring (cache vs. local source)

Key abstractions

Type File Purpose
Manager agent/proxycfg/manager.go Owns one state per registered proxy; broadcasts snapshots
state agent/proxycfg/state.go The per-proxy state machine: subscribes to deps, debounces, builds snapshots
ConfigSnapshot agent/proxycfg/snapshot.go The output contract; one per proxy. Sub-fields per gateway kind
DataSources agent/proxycfg/data_sources.go The interface the state machine uses to fetch deps; satisfied by proxycfg-glue
Watcher agent/proxycfg/manager.go Subscriber that receives snapshot deltas
kindHandler interface per-kind <kind>.go files Per-proxy-kind population logic

How it works

graph LR
    subgraph Inputs
        Catalog[Catalog<br/>health subscribes]
        Config[Config entries<br/>discovery-chain compile]
        Intent[Intentions<br/>service-intentions]
        Roots[Connect CA roots]
        Leaf[Leaf cert manager]
        Peer[Peering trust bundles]
        Mesh[Mesh config]
        JWT[JWT providers]
    end
    Catalog & Config & Intent & Roots & Leaf & Peer & Mesh & JWT -->|DataSources interface| State[state.run loop<br/>debounce+merge]
    State -->|build per kind| Snap[ConfigSnapshot]
    Snap -->|broadcast| Watchers[xDS server, dataplane consumers]

Each proxy's state loop:

  1. Identifies the proxy kind (connect-proxy, mesh-gateway, ingress-gateway, terminating-gateway, api-gateway).
  2. Issues data-source watches for everything that kind needs.
  3. As updates arrive, it merges them into the current snapshot, marking the snapshot incomplete until all required dependencies have produced a value.
  4. Once complete, it broadcasts the snapshot to every watcher (xDS, dataplane bridge, in-process consumers).
  5. Subsequent updates produce new snapshots; consumers diff them.

The debounce and merging logic is the trickiest part: a single config-entry change can fan out to dozens of upstream/cluster updates, and the state machine has to wait until all of them have settled before emitting a new snapshot.

Per-kind population

Kind Builder Notes
connect-proxy connect_proxy.go The default sidecar case; watches one inbound + N upstream discovery chains
mesh-gateway mesh_gateway.go Watches DC peers, federation state, and exported services
ingress-gateway ingress_gateway.go Watches ingress-gateway config entries + per-listener service lists
terminating-gateway terminating_gateway.go Watches associated services and their CA roots/leafs
api-gateway api_gateway.go Watches api-gateway config + http-route + tcp-route + inline-certificate / file-system-certificate

Data sources

The DataSources interface (a struct of named sub-interfaces) abstracts fetching. Implementations:

  • Cache-backed (agent/proxycfg-glue/): uses agent/cache/ for catalog, health, intentions, etc. Default in client agents.
  • Local (server-side): reads directly from the state store when running embedded in a server.

This indirection is what allows the same proxycfg machinery to run on a client agent and inside the dataplane sidecar process, where the dataplane has its own gRPC channel back to a server.

Snapshot shape

ConfigSnapshot is a tagged union over kinds. Common fields include Roots (CA roots), Leaf (leaf cert), and MeshConfig. Per-kind fields hold:

  • For sidecars: Upstreams, DiscoveryChain per upstream, EndpointEvents, IntentionsMatchAllow, ...
  • For mesh gateways: WatchedServices, WatchedGateways, FedStateGateways, PeeringTrustBundles.
  • For ingress gateways: Listeners, WatchedDiscoveryChains, Hosts.
  • For api gateways: BoundListeners, Routes, Certificates, JWTProviders.

Integration points

  • Source of truth: the proxy config manager doesn't write anything — it only reads. State is owned by the catalog, config-entry, intention, and CA stores.
  • Consumers: the xDS server and (via agent/grpc-external/) the dataplane subscribe through the manager. There is also a tap for hot-reload tests in proxycfg/manager_test.go.
  • Watchers' lifecycle: when a proxy deregisters or the agent shuts down, the manager cancels all open watches.

Entry points for modification

  • Add a new dependency for an existing kind: add a sub-interface to DataSources, plumb the watch in state.<kind>, and update the snapshot type and the consumers (xDS, dataplane).
  • Add a new proxy kind: define a new value of structs.ServiceKind, add a builder file, register it in the manager's kind switch, and update the xDS resource generators.
  • Optimize a hot path: look at state.go for the merging logic — most snapshot rebuilds touch a fraction of fields and there is room for finer-grained invalidation.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Proxy config (proxycfg) – Consul wiki | Factory