Consul — API Surface#

API types#

REST (v1), REST (v2 via HTTP→gRPC proxy), gRPC (external), gRPC (internal multiplexed), net/rpc (internal), DNS, CLI

Consul exposes one of the richest API surfaces of any infrastructure tool in the Go ecosystem: over 130 HTTP endpoints, a growing suite of gRPC services, a custom DNS interface, and a deep CLI with ~100 subcommands — all from a single binary.


REST/HTTP API (v1)#

  • Router: stdlib net/http ServeMux — no third-party router. All routes registered via init() in agent/http_register.go:6.
  • Route registration: Single global init() block calls registerEndpoint(path, methods, handlerFn) for every endpoint. The endpoints map is iterated at server start in agent/http.go:261. No annotations or auto-discovery.
  • Middleware chain (outer → inner):
    1. cleanhttp.PrintablePathCheckHandler — rejects non-printable chars in URLs (security)
    2. withRemoteAddrHandler — injects remote addr into context
    3. ensureContentTypeHeader — explicitly sets Content-Type to prevent XSS
    4. s.wrap() — per-handler wrapper that: checks allowed HTTP methods, parses ACL token, records metrics, handles blocking query parameters, and JSON-encodes the response
    5. GZIP (agent/http.go:218) for compressible responses
  • Authentication: ACL token accepted via X-Consul-Token header or ?token= query parameter (agent/http.go:1071). The query parameter variant is deprecated and logged as a warning. A default agent token is applied when no token is present.
  • Key endpoint groups (from agent/http_register.go):
PrefixEndpointsNotes
/v1/acl/~15Bootstrap, login/logout, token/policy/role/binding-rule/auth-method CRUD
/v1/agent/~20Self, services, checks, join/leave, health, Connect CA leaf certs, token management
/v1/catalog/~10Node/service registration, datacenter/node/service queries
/v1/health/~6Checks by node, service, state; Connect health
/v1/kv/1 (prefix)Full KV store: GET/PUT/DELETE with CAS, blocking queries
/v1/connect/~5Intentions, CA roots/config
/v1/config/2Config entry read/write
/v1/peering/~4Cluster peering: generate token, establish, read, list
/v1/operator/~8Raft peers, keyring, autopilot, usage, utilization
/v1/session/~6Session create/destroy/renew/list
/v1/snapshot1GET=save, PUT=restore
/v1/txn1Multi-key atomic transaction
/v1/coordinate/~4Network coordinate queries and update
/v1/query/2Prepared queries
/v1/event/2Custom event fire + list
/v1/discovery-chain/1Service discovery chain read
/v1/status/2Raft leader, peer list
/v1/internal/~10UI-specific aggregated views, ACL authorize, RPC methods

Total: ~130 routes. Blocking query support (?wait=, ?index=) is implemented generically in s.wrap() for all endpoints that set QueryMeta.Index.


REST/HTTP API (v2 — resource API)#

  • Prefix: /api/
  • Handler: agent/http.go:277 routes /api/* to resourcehttp.NewHandler() which translates HTTP requests into ResourceService gRPC calls.
  • Style: Kubernetes-style resource API. URLs follow the pattern /api/{group}/{version}/{kind}/{namespace}/{name}. Supports GET (read), PUT (write), DELETE, and list (GET without name).
  • Authentication: Delegates to the same parseToken function as v1.
  • This is the HTTP face of the v2 gRPC ResourceService. Consul ships both a native gRPC interface and this HTTP translation layer simultaneously.

gRPC API (external — port 8502 / 8503 TLS)#

Proto files live in proto-public/ (public contract) and proto/private/ (internal). The external gRPC server is created in agent/agent.go:968 and services are registered via server_grpc.go.

Services registered on the external gRPC server:#

ServiceProto fileKey RPCsPurpose
ResourceServiceproto-public/pbresource/resource.protoRead, Write, WriteStatus, List, ListByOwner, Delete, WatchList, MutateAndValidateGeneric v2 resource CRUD + server-streaming watch
DataplaneServiceproto-public/pbdataplane/dataplane.protoGetSupportedDataplaneFeatures, GetEnvoyBootstrapParamsBootstrap Envoy sidecars (consul-dataplane uses this)
ConnectCAServiceproto-public/pbconnectca/ca.protoWatchRoots (stream), SignStream active CA roots; sign leaf certs
ServerDiscoveryServiceproto-public/pbserverdiscovery/serverdiscovery.protoWatchServers (stream)Stream live server addresses for client load balancing
ACLServiceproto-public/pbacl/acl.proto(ACL operations over gRPC)External ACL management
PeeringServiceproto/private/pbpeering/peering.protoGenerate token, establish, read, list, deleteCluster peering lifecycle
PeerStreamServiceproto/private/pbpeerstream/peerstream.protoStreamResources (bidirectional stream)Cross-cluster resource sync via streaming RPC
OperatorServiceproto/private/pboperator/operator.protoServerHealth, AutopilotStateOperator diagnostics
ConfigEntryService(internal)(config entry ops)Config entry management over gRPC
AggregatedDiscoveryServiceEnvoy xDS v3DeltaAggregatedResources (bidirectional stream)xDS for Envoy sidecar proxy configuration
DNSServiceproto-public/pbdns/dns.protoQueryDNS over gRPC (new, in addition to UDP/TCP DNS)
StateChangeSubscriptionproto/private/pbsubscribe/subscribe.protoSubscribe (stream)Internal event streaming (blocking-query replacement)

gRPC Interceptors (external server — agent/consul/server_grpc.go:119):

  • Rate limiter (in-tap handler, pre-gRPC: ServerRateLimiterMiddleware)
  • Panic recovery (go-grpc-middleware/recovery)
  • Stats handler for metrics (agentmiddleware.NewStatsHandler)
  • Active stream counter (stream interceptor)
  • ACL token forwarding (metadata x-consul-token)

gRPC Interceptors (internal multiplexed server — agent/grpc-internal/handler.go:38):

  • Rate limiter
  • Panic recovery
  • Stats handler
  • Active stream counter

gRPC reflection is registered on the external server (reflection.Register), enabling grpcurl and similar tools to introspect services at runtime.


DNS API (port 8600)#

  • Implementation: agent/dns.go — custom DNS server using miekg/dns (not a framework).
  • Handler registration: srv.mux.HandleFunc("arpa.", srv.handlePtr) and srv.mux.HandleFunc(srv.domain, srv.handleQuery) (and alt domain if configured).
  • Supported query types: A, AAAA, CNAME, SRV, TXT, PTR
  • Lookup patterns:
    • <service>.service[.datacenter].consul — service discovery
    • <id>.node[.datacenter].consul — node lookup
    • <id>.query[.datacenter].consul — prepared query execution
    • <service>.connect.consul — Connect-capable endpoints only
    • <service>.ingress.consul — ingress gateway endpoints
    • Reverse lookup for .in-addr.arpa. PTR records
  • Recursive resolution: Forwarded to configured upstream resolvers when the domain is not .consul.
  • Also exposed as gRPC: DNSService proto added in recent versions for DNS-over-gRPC.

net/rpc API (internal — multiplexed on port 8300)#

Used exclusively for server-to-server and agent-to-server communication. Not exposed as a public API — clients should use HTTP or gRPC instead.

Registered handlers (agent/consul/server_register.go):

HandlerResponsibility
ACLToken/policy/role resolution, replication
CatalogNode/service/check registration and queries
CoordinateNetwork coordinate updates and queries
ConfigEntryService mesh configuration entries
ConnectCACA root management, leaf cert signing
FederationStateWAN federation mesh gateway state
DiscoveryChainResolved discovery chain computation
HealthHealth check queries (blocking-query capable)
IntentionService intention management
InternalDiagnostic and internal-use RPC
KVSKey-value store operations
OperatorRaft/autopilot management
PreparedQueryPrepared query CRUD and execution
SessionSession create/destroy/renew
StatusRaft leader/peer status
TxnMulti-operation atomic transactions
AutoEncrypt(insecure server) TLS auto-enrollment for agents
AutoConfig(insecure server) JWT-based client bootstrap

CLI#

  • Framework: mitchellh/cli — not Cobra. Commands implement cli.Command interface with Run(args []string) int. Help text is returned from Synopsis() / Help() methods.
  • Registration: All commands registered in command/registry.go:RegisteredCommands() via a flat entry{name, factory} slice. Enterprise commands appended via registerEnterpriseCommands().
  • Subcommand nesting: Space-separated names ("acl token create") — mitchellh/cli resolves these into a tree automatically.

Top-level command groups (from command/registry.go):

GroupSubcommandsPurpose
aclbootstrap, policy, role, token, auth-method, binding-rule, set-agent-token, templated-policyACL management (~25 subcommands)
agentRun the Consul agent
catalogdatacenters, nodes, servicesCatalog read
configread, write, list, deleteConfig entry management
connectca get-config/set-config, proxy, envoy, envoy pipe-bootstrap, expose, redirect-trafficService mesh tooling
intentioncheck, create, delete, get, list, matchIntention management
kvget, put, delete, import, exportKV store operations
operatorautopilot get-config/set-config/state, raft list-peers/remove-peer/transfer-leader, usage instances, utilizationCluster operations
peeringgenerate-token, establish, read, list, delete, exported-servicesCluster peering
resourceread, write, list, delete (+ -grpc variants being deprecated)v2 resource API
servicesregister, deregister, export, exported-services, imported-servicesService management
snapshotsave, restore, inspect, decodeRaft snapshot management
tlsca create, cert createTLS certificate generation
troubleshootproxy, upstreams, portsMesh debugging
debugCapture agent diagnostics bundle
exec, event, lock, watch, monitorRuntime utility commands
join, leave, force-leave, reload, maint, members, info, rtt, keygen, keyringAgent/cluster operations

Flag patterns: Each command defines its own flags via stdlib flag.FlagSet. No global persistent flags (mitchellh/cli does not support them). Environment variable binding is done manually per-command (e.g., CONSUL_HTTP_ADDR, CONSUL_HTTP_TOKEN). The command/cli/cli.go file provides a shared HTTPFlags struct that all network-touching commands embed for common HTTP flags.


Library API#

  • Public Go client: github.com/hashicorp/consul/api — a separate, well-maintained Go client library for the HTTP v1 API. This package is not in the main binary but is the canonical way to interact with Consul from Go code.
  • Public packages in the repository:
    • api/ — HTTP v1 client (high-level Go client with retry, backoff, and blocking query support)
    • proto-public/ — Published protobuf definitions for the gRPC external API
    • sdk/ — Low-level utilities (testutil, flags) sometimes used by third-party integrations
  • Backward compatibility: The v1 HTTP API is considered stable. Proto-public uses MPL-2.0 license (vs BUSL-1.1 for internal protos) signaling intentional public stability. The proto-public/pbresource/resource.proto ResourceService uses explicit group_version in its Type message for API versioning.

Notable API surface observations#

  1. Five protocol layers on one binary: REST v1, REST v2 (via gRPC proxy), gRPC external, net/rpc internal, and DNS — all served simultaneously from one consul agent process. Each targets a different consumer: operators (HTTP+CLI), service mesh data plane (gRPC/xDS), internal cluster communication (net/rpc), and service discovery (DNS).

  2. Endpoint registration via init(): All 130+ HTTP routes are registered in a single init() block in agent/http_register.go. This is an unusual but effective pattern — the routes are statically defined at compile time with no runtime registration API. Adding a new endpoint requires editing this one file.

  3. v1 vs v2 API coexistence: The /v1/ REST API and the /api/ v2 REST API (backed by gRPC ResourceService) exist side by side. The v2 API follows Kubernetes GVK (Group/Version/Kind) conventions: PUT /api/catalog/v2beta1/Service/default/web. This is a deliberate migration path: teams can write v2-style features without removing v1.

  4. Rate limiting at the gRPC in-tap layer: The rate limiter interceptor fires before a goroutine is allocated for the RPC, making it more efficient than a conventional unary interceptor. This is an architecturally interesting choice — the rate.Handler registers as a grpc.InTapHandle (agent/grpc-internal/handler.go:41).

  5. gRPC reflection always on: Registering reflection.Register(s.externalGRPCServer) means any gRPC client can enumerate all services and their schemas at runtime — a very operator-friendly choice for a infrastructure tool, but worth noting for security posture in production.