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/httpServeMux— no third-party router. All routes registered viainit()inagent/http_register.go:6. - Route registration: Single global
init()block callsregisterEndpoint(path, methods, handlerFn)for every endpoint. Theendpointsmap is iterated at server start inagent/http.go:261. No annotations or auto-discovery. - Middleware chain (outer → inner):
cleanhttp.PrintablePathCheckHandler— rejects non-printable chars in URLs (security)withRemoteAddrHandler— injects remote addr into contextensureContentTypeHeader— explicitly setsContent-Typeto prevent XSSs.wrap()— per-handler wrapper that: checks allowed HTTP methods, parses ACL token, records metrics, handles blocking query parameters, and JSON-encodes the response- GZIP (
agent/http.go:218) for compressible responses
- Authentication: ACL token accepted via
X-Consul-Tokenheader 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):
| Prefix | Endpoints | Notes |
|---|---|---|
/v1/acl/ | ~15 | Bootstrap, login/logout, token/policy/role/binding-rule/auth-method CRUD |
/v1/agent/ | ~20 | Self, services, checks, join/leave, health, Connect CA leaf certs, token management |
/v1/catalog/ | ~10 | Node/service registration, datacenter/node/service queries |
/v1/health/ | ~6 | Checks by node, service, state; Connect health |
/v1/kv/ | 1 (prefix) | Full KV store: GET/PUT/DELETE with CAS, blocking queries |
/v1/connect/ | ~5 | Intentions, CA roots/config |
/v1/config/ | 2 | Config entry read/write |
/v1/peering/ | ~4 | Cluster peering: generate token, establish, read, list |
/v1/operator/ | ~8 | Raft peers, keyring, autopilot, usage, utilization |
/v1/session/ | ~6 | Session create/destroy/renew/list |
/v1/snapshot | 1 | GET=save, PUT=restore |
/v1/txn | 1 | Multi-key atomic transaction |
/v1/coordinate/ | ~4 | Network coordinate queries and update |
/v1/query/ | 2 | Prepared queries |
/v1/event/ | 2 | Custom event fire + list |
/v1/discovery-chain/ | 1 | Service discovery chain read |
/v1/status/ | 2 | Raft leader, peer list |
/v1/internal/ | ~10 | UI-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:277routes/api/*toresourcehttp.NewHandler()which translates HTTP requests intoResourceServicegRPC 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
parseTokenfunction 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:#
| Service | Proto file | Key RPCs | Purpose |
|---|---|---|---|
ResourceService | proto-public/pbresource/resource.proto | Read, Write, WriteStatus, List, ListByOwner, Delete, WatchList, MutateAndValidate | Generic v2 resource CRUD + server-streaming watch |
DataplaneService | proto-public/pbdataplane/dataplane.proto | GetSupportedDataplaneFeatures, GetEnvoyBootstrapParams | Bootstrap Envoy sidecars (consul-dataplane uses this) |
ConnectCAService | proto-public/pbconnectca/ca.proto | WatchRoots (stream), Sign | Stream active CA roots; sign leaf certs |
ServerDiscoveryService | proto-public/pbserverdiscovery/serverdiscovery.proto | WatchServers (stream) | Stream live server addresses for client load balancing |
ACLService | proto-public/pbacl/acl.proto | (ACL operations over gRPC) | External ACL management |
PeeringService | proto/private/pbpeering/peering.proto | Generate token, establish, read, list, delete | Cluster peering lifecycle |
PeerStreamService | proto/private/pbpeerstream/peerstream.proto | StreamResources (bidirectional stream) | Cross-cluster resource sync via streaming RPC |
OperatorService | proto/private/pboperator/operator.proto | ServerHealth, AutopilotState | Operator diagnostics |
ConfigEntryService | (internal) | (config entry ops) | Config entry management over gRPC |
AggregatedDiscoveryService | Envoy xDS v3 | DeltaAggregatedResources (bidirectional stream) | xDS for Envoy sidecar proxy configuration |
DNSService | proto-public/pbdns/dns.proto | Query | DNS over gRPC (new, in addition to UDP/TCP DNS) |
StateChangeSubscription | proto/private/pbsubscribe/subscribe.proto | Subscribe (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 usingmiekg/dns(not a framework). - Handler registration:
srv.mux.HandleFunc("arpa.", srv.handlePtr)andsrv.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:
DNSServiceproto 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):
| Handler | Responsibility |
|---|---|
ACL | Token/policy/role resolution, replication |
Catalog | Node/service/check registration and queries |
Coordinate | Network coordinate updates and queries |
ConfigEntry | Service mesh configuration entries |
ConnectCA | CA root management, leaf cert signing |
FederationState | WAN federation mesh gateway state |
DiscoveryChain | Resolved discovery chain computation |
Health | Health check queries (blocking-query capable) |
Intention | Service intention management |
Internal | Diagnostic and internal-use RPC |
KVS | Key-value store operations |
Operator | Raft/autopilot management |
PreparedQuery | Prepared query CRUD and execution |
Session | Session create/destroy/renew |
Status | Raft leader/peer status |
Txn | Multi-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 implementcli.Commandinterface withRun(args []string) int. Help text is returned fromSynopsis()/Help()methods. - Registration: All commands registered in
command/registry.go:RegisteredCommands()via a flatentry{name, factory}slice. Enterprise commands appended viaregisterEnterpriseCommands(). - Subcommand nesting: Space-separated names (
"acl token create") — mitchellh/cli resolves these into a tree automatically.
Top-level command groups (from command/registry.go):
| Group | Subcommands | Purpose |
|---|---|---|
acl | bootstrap, policy, role, token, auth-method, binding-rule, set-agent-token, templated-policy | ACL management (~25 subcommands) |
agent | — | Run the Consul agent |
catalog | datacenters, nodes, services | Catalog read |
config | read, write, list, delete | Config entry management |
connect | ca get-config/set-config, proxy, envoy, envoy pipe-bootstrap, expose, redirect-traffic | Service mesh tooling |
intention | check, create, delete, get, list, match | Intention management |
kv | get, put, delete, import, export | KV store operations |
operator | autopilot get-config/set-config/state, raft list-peers/remove-peer/transfer-leader, usage instances, utilization | Cluster operations |
peering | generate-token, establish, read, list, delete, exported-services | Cluster peering |
resource | read, write, list, delete (+ -grpc variants being deprecated) | v2 resource API |
services | register, deregister, export, exported-services, imported-services | Service management |
snapshot | save, restore, inspect, decode | Raft snapshot management |
tls | ca create, cert create | TLS certificate generation |
troubleshoot | proxy, upstreams, ports | Mesh debugging |
debug | — | Capture agent diagnostics bundle |
exec, event, lock, watch, monitor | — | Runtime utility commands |
join, leave, force-leave, reload, maint, members, info, rtt, keygen, keyring | — | Agent/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 APIsdk/— 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.0license (vsBUSL-1.1for internal protos) signaling intentional public stability. Theproto-public/pbresource/resource.protoResourceService uses explicitgroup_versionin itsTypemessage for API versioning.
Notable API surface observations#
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 agentprocess. Each targets a different consumer: operators (HTTP+CLI), service mesh data plane (gRPC/xDS), internal cluster communication (net/rpc), and service discovery (DNS).Endpoint registration via
init(): All 130+ HTTP routes are registered in a singleinit()block inagent/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.v1 vs v2 API coexistence: The
/v1/REST API and the/api/v2 REST API (backed by gRPCResourceService) 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.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.Handlerregisters as agrpc.InTapHandle(agent/grpc-internal/handler.go:41).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.