Open-Source Wikis

/

Consul

/

Systems

/

Agent runtime

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:

  1. Pulls the catalog view of "what's registered for this node" from a server.
  2. Diffs against local.State.
  3. 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 in agent/cache-types/ (catalog services, health, prepared queries, intentions, leaf certs, roots, ...). Each type defines a Fetch that 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.ErrPermissionDenied and other sentinel errors into proper HTTP status codes
  • Blocking-query metadata (X-Consul-Index, X-Consul-LastContact, X-Consul-KnownLeader)
  • Pretty-printing when ?pretty is 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.consulconsul.Health.ServiceNodes for service web in the local DC.
  • web.service.dc1.consul → query DC dc1 (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 -server is set, Agent.delegate becomes 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, pbserverdiscovery are exposed via agent/grpc-external/ for use by Consul Dataplane.
  • Telemetry: lib/telemetry/ builds a *metrics.Metrics sink with Prometheus, statsite, statsd, Datadog, or Circonus endpoints. Every subsystem reports metrics via package-level helpers.
  • TLS: tlsutil.Configurator tracks live TLS configuration and is rotated on consul reload so 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 in agent/setup.go (or agent/agent.go New), and run/stop it in Start/Stop.
  • To add an HTTP endpoint: new package or function in agent/, register in agent/http_register.go, implement the handler with s.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.go calls acl.Authorizer returned by (*Agent).resolveTokenAndDefaultMeta.

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

Agent runtime – Consul wiki | Factory