hashicorp/consul
Agent runtime
The Consul agent is the long-running process that every node in the cluster boots. It hosts the HTTP and DNS APIs, holds local state, runs health checks, manages Connect proxies, and forwards to a Consul server when authoritative state is needed. The same agent code runs on both clients and servers — server mode just lights up additional subsystems documented in Server and Raft.
Purpose
agent/agent.go defines the Agent struct, the centerpiece of the runtime. It owns HTTP servers, DNS servers, the gRPC proxy targets, the local registration state, the cache, and the proxy config manager. It is large by design: the agent has accumulated functionality since 2013 (the file is 158 KB), and most refactors push complexity into sub-packages while keeping Agent as the wiring hub.
Directory layout
agent/
├── agent.go # The Agent struct and lifecycle (158 KB)
├── setup.go # Boot-time wiring (loggers, telemetry, telemetry pipeline, DNS, HTTP, gRPC servers)
├── http.go # HTTP server scaffold: routing, auth, blocking-query glue (38 KB)
├── http_register.go # Where every /v1/... handler is registered
├── dns.go # DNS request handler and resolver (72 KB)
├── *_endpoint.go # HTTP endpoint implementations (catalog, health, kvs, txn, etc.)
├── apiserver.go # Lifecycle for the HTTP server (start/stop/wait)
├── ae/ # Anti-entropy: push local state to servers
├── auto-config/ # Auto-bootstrap of TLS material + initial config
├── blockingquery/ # Long-poll helpers used across endpoints
├── cache/, cache-types/, cacheshim/ # In-memory cache for RPC results
├── checks/ # Health check engines (TTL, HTTP, TCP, script, alias, ...)
├── config/ # Config file parser, defaults, RuntimeConfig builder
├── consul/ # Server-only logic (Raft, FSM, RPC endpoints, leader loops)
├── dns/ # New DNS subsystem (in progress, alongside the legacy dns.go)
├── envoyextensions/ # Plumbing for the envoyextensions go-module
├── exec/ # `consul exec` remote execution helpers
├── grpc-external/ # External-facing gRPC services
├── grpc-internal/ # Internal control-plane gRPC services
├── grpc-middleware/ # Shared gRPC interceptors
├── leafcert/ # Leaf cert cache for mesh services
├── local/ # Local state for services/checks/proxies
├── log-drop/ # Log dropping under load
├── metadata/, metrics/ # Metric registration and node metadata helpers
├── mock/ # Test mocks
├── netutil/ # Bind address helpers
├── proxycfg/, proxycfg-glue/, proxycfg-sources/ # Proxy snapshot manager
├── pool/ # RPC connection pool to servers
├── router/ # Server selection and area routing
├── rpc/, rpcclient/ # RPC client helpers
├── service_manager/ # Reconciles service registrations with config-entries
├── sidecar_service.go # Implicit sidecar registration
├── structs/ # Shared structs (config entries, ACL, tokens, ...)
├── submatview/ # Materialized view from streaming subscribe
├── token/ # ACL/agent token lifecycle
├── uiserver/ # Embedded UI handler
├── userevent.go # Custom user events on top of gossip
├── xds/ # Envoy xDS server
└── ...Key abstractions
| Type | File | Purpose |
|---|---|---|
Agent |
agent/agent.go |
Owns every subsystem; the lifetime root |
delegate |
agent/agent.go |
Interface satisfied by *consul.Server or *consul.Client for RPC dispatch |
BaseDeps |
agent/setup.go |
Dependency bag passed to agent.New; contains logger, telemetry, cache, tokens |
HTTPHandlers |
agent/http.go |
Wraps every endpoint with auth, content-type, error mapping |
local.State |
agent/local/state.go |
The local catalog: services + checks + Connect proxies known to this agent |
cache.Cache |
agent/cache/cache.go |
Generic cache with per-type fetchers; supports blocking refresh |
proxycfg.Manager |
agent/proxycfg/manager.go |
Holds one watcher per proxy, builds and ships ConfigSnapshots |
xds.Server |
agent/xds/server.go |
Implements the Envoy ADS gRPC service |
dns.Server |
agent/dns.go |
Old DNS server; resolves service/node queries via local agent + RPC |
tlsutil.Configurator |
tlsutil/config.go |
One source of truth for incoming/outgoing TLS |
token.Store |
agent/token/store.go |
Holds the agent token, default token, replication token, dataplane tokens |
consul.Client |
agent/consul/client.go |
Client-mode delegate: forwards RPCs to servers via Serf-discovered pool |
consul.Server |
agent/consul/server.go |
Server-mode delegate: hosts Raft, RPC endpoints, leader loops |
How an agent boots
sequenceDiagram
participant Main as command/agent
participant Setup as agent/setup.go
participant Agent as agent/agent.go
participant Local as agent/local
participant Server as consul.Server (if -server)
participant HTTP as agent/http.go
participant DNS as agent/dns.go
Main->>Setup: NewBaseDeps(cfg, ...)
Setup-->>Main: BaseDeps {logger, cache, tokens, ...}
Main->>Agent: New(baseDeps)
Agent->>Server: consul.NewServer / NewClient
Agent->>Local: NewState
Agent->>HTTP: setupHTTPListeners
Agent->>DNS: setupDNSListeners
Agent-->>Main: Agent ready
Main->>Agent: Start(ctx)
Agent->>Server: WaitForLeader (then run leader loops)
Agent->>Local: anti-entropy goroutine
Agent->>HTTP: Serve()
Agent->>DNS: Serve()Anti-entropy
Local state on each agent (services and checks registered through the HTTP API or config files) must mirror what's in the catalog. agent/ae/ runs a periodic reconciliation that:
- Pulls the catalog view of "what's registered for this node" from a server.
- Diffs against
local.State. - Sends RPC adds/updates/deletes to bring the catalog in sync.
The same loop reconciles checks and Connect proxy registrations. Anti-entropy backs off on errors and reseeds when the agent reconnects after a partition.
Cache and submatview
To avoid hammering servers with the same blocking query, the agent owns:
agent/cache/— a generic in-memory cache parameterized by cache types registered inagent/cache-types/(catalog services, health, prepared queries, intentions, leaf certs, roots, ...). Each type defines aFetchthat does a blocking RPC and a key derivation.agent/submatview/— a materialized-view cache that maintains state by consuming a gRPC streaming subscribe channel from the server. Used for high-fanout topics like the catalog and config entries.
The proxy config manager and most service-discovery readers go through these caches, so a single agent with thousands of subscribers makes only one upstream subscription per topic.
HTTP API surface
Every handler is registered in agent/http_register.go. The pattern:
s.handleFuncMetrics("/v1/agent/services", s.wrap(s.AgentServices))s.wrap (in agent/http.go) handles:
- ACL token extraction (header
X-Consul-Token, query?token=, or env) - Datacenter, partition, and namespace header parsing
- JSON encoding of replies
- Conversion of
acl.ErrPermissionDeniedand other sentinel errors into proper HTTP status codes - Blocking-query metadata (
X-Consul-Index,X-Consul-LastContact,X-Consul-KnownLeader) - Pretty-printing when
?prettyis set
Endpoint implementations live in agent/<thing>_endpoint.go (e.g., agent/catalog_endpoint.go, agent/kvs_endpoint.go, agent/intentions_endpoint.go). Most of them simply translate the HTTP request into an RPC call against the Consul server (the delegate interface).
DNS interface
The legacy DNS server is implemented in agent/dns.go. Queries to service.consul, node.consul, and the addr.<ip>.consul synthetic family are mapped to catalog lookups:
web.service.consul→consul.Health.ServiceNodesfor servicewebin the local DC.web.service.dc1.consul→ query DCdc1(forwarded over WAN if local agent is in another DC).- Node lookups, prepared-query DNS, and tag-based filtering are handled in the same file.
A newer subpackage agent/dns/ is being grown to replace the monolithic dns.go; both run today.
Proxy config and xDS
For each Connect-enabled service registered locally, the agent's proxy config manager (agent/proxycfg/manager.go) starts a goroutine that watches the catalog, intentions, JWT providers, mesh config, and so on, builds a ConfigSnapshot, and broadcasts it to the xDS server. Envoy connects over delta-xDS; the agent translates the snapshot into clusters/listeners/routes/endpoints/secrets in agent/xds/{clusters,listeners,routes,endpoints,secrets}.go. See Proxy config and xDS server for details.
Integration points
- Server-mode delegation: when
-serveris set,Agent.delegatebecomes a*consul.Server. The same RPC interface is used in either mode, so client-mode RPCs forward over the Serf-discovered pool while server-mode RPCs run in-process. - gRPC dataplane:
proto-public/pbdataplane,pbdns,pbserverdiscoveryare exposed viaagent/grpc-external/for use by Consul Dataplane. - Telemetry:
lib/telemetry/builds a*metrics.Metricssink with Prometheus, statsite, statsd, Datadog, or Circonus endpoints. Every subsystem reports metrics via package-level helpers. - TLS:
tlsutil.Configuratortracks live TLS configuration and is rotated onconsul reloadso that cert renewal doesn't restart the agent.
Entry points for modification
- To wire a new subsystem into the agent: add a field to
Agent, initialize it inagent/setup.go(oragent/agent.goNew), and run/stop it inStart/Stop. - To add an HTTP endpoint: new package or function in
agent/, register inagent/http_register.go, implement the handler withs.wrap(...). - To change DNS behavior: start by reading the request type switch at the top of
agent/dns.go(d.dispatch). - To touch local state: all mutations go through
agent/local/state.go; the AE loop will pick them up. - To plumb a new ACL-aware action: see how
agent/agent_endpoint.gocallsacl.Authorizerreturned by(*Agent).resolveTokenAndDefaultMeta.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.