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:
- Identifies the proxy kind (
connect-proxy,mesh-gateway,ingress-gateway,terminating-gateway,api-gateway). - Issues data-source watches for everything that kind needs.
- As updates arrive, it merges them into the current snapshot, marking the snapshot incomplete until all required dependencies have produced a value.
- Once complete, it broadcasts the snapshot to every watcher (xDS, dataplane bridge, in-process consumers).
- 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/): usesagent/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,DiscoveryChainper 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 inproxycfg/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 instate.<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.gofor 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.