Consul — Structure#
Layout pattern#
Custom Monorepo (not Standard Go Layout)
Consul deviates from the canonical cmd/-based Go layout. There is a single main.go at the root that produces one consul binary. The project is structured as a multi-module monorepo: five sub-modules (api/, sdk/, proto-public/, envoyextensions/, troubleshoot/) have their own go.mod files and are referenced from the root via replace directives. The dominant package organizing principle is by feature domain (agent/, acl/, connect/, command/) rather than by layer.
Directory map#
consul/
├── main.go # Single entry point for the consul binary
├── acl/ # ACL policy engine and token evaluation
│ └── resolver/ # ACL resolver implementations
├── agent/ # Core agent logic (~40 sub-packages; the heart of the project)
│ ├── ae/ # Anti-entropy sync (local vs. server state)
│ ├── auto-config/ # Automatic configuration via JWT/TLS bootstrapping
│ ├── cache/ # Client-side caching layer with blocking queries
│ ├── checks/ # Health check runners (HTTP, TCP, script, gRPC, etc.)
│ ├── config/ # Configuration parsing and validation
│ ├── configentry/ # Config entry management (service defaults, etc.)
│ ├── connect/ # Agent-side Connect/service-mesh logic
│ ├── consul/ # Server-mode agent: Raft FSM, RPC handlers, state store
│ ├── dns/ # DNS server implementation
│ ├── grpc-external/ # gRPC services exposed externally (v1 and v2)
│ ├── grpc-internal/ # Internal gRPC (server-to-server)
│ ├── grpc-middleware/ # gRPC interceptors (rate limiting, ACL, logging)
│ ├── leafcert/ # Leaf certificate manager for Connect TLS
│ ├── local/ # Local state manager (services/checks registered on this agent)
│ ├── proxycfg/ # Proxy configuration watchers (feeds xDS to Envoy)
│ ├── proxycfg-glue/ # Glue layer connecting proxycfg to storage backends
│ ├── proxycfg-sources/ # Data sources for proxy config (catalog, config entries)
│ ├── router/ # Server routing and area management
│ ├── rpc/ # RPC client pool and load balancing
│ ├── rpcclient/ # High-level RPC client wrappers
│ ├── structs/ # Shared data structures (the project's "types" package)
│ ├── token/ # Token store and management
│ ├── uiserver/ # Embedded web UI HTTP server
│ └── xds/ # Envoy xDS (v2/v3) delta and full-state servers
├── api/ # [Sub-module] Public Go client library for Consul HTTP API
│ └── watch/ # Watch plan helpers
├── bench/ # Benchmarks (config parsing, etc.)
├── build-support/ # Docker files, shell scripts, build tooling
├── command/ # CLI command implementations (~35 commands)
│ ├── acl/ # ACL token/policy/role CRUD commands
│ ├── agent/ # `consul agent` command
│ ├── catalog/ # Catalog query commands
│ ├── connect/ # Connect intention commands
│ ├── kv/ # KV store commands
│ ├── operator/ # Raft/autopilot operator commands
│ ├── peering/ # Cluster peering commands
│ ├── snapshot/ # Snapshot save/restore commands
│ ├── tls/ # TLS cert generation commands
│ ├── flags/ # Shared flag definitions
│ ├── registry.go # Central command registry
│ └── ... # ~25 more command sub-packages
├── connect/ # Connect service mesh: cert generation, proxy protocol
│ ├── certgen/ # Certificate generation utilities
│ └── proxy/ # Proxy configuration types
├── docs/ # Architecture and design documentation (markdown)
│ └── v2-architecture/ # Docs for the new resource-based v2 architecture
├── envoyextensions/ # [Sub-module] Envoy extension framework
│ ├── extensioncommon/ # Base types for Envoy extensions
│ └── xdscommon/ # Shared xDS utilities for extensions
├── grafana/ # Grafana dashboard JSON definitions
├── grpcmocks/ # Generated gRPC mocks for proto-public
├── internal/ # Internal-only packages (not importable externally)
│ ├── controller/ # Resource controller framework (v2 architecture)
│ │ ├── cache/ # Controller cache with indexing
│ │ └── dependency/ # Dependency tracking for controllers
│ ├── dnsutil/ # DNS utilities
│ ├── gossip/ # Gossip/Serf and RTT library wrappers
│ ├── go-sso/ # OIDC/SSO authentication library (vendored-in)
│ ├── multicluster/ # Multi-cluster / federation resource types and controllers
│ ├── protohcl/ # Protobuf <-> HCL conversion
│ ├── protoutil/ # Protobuf utility helpers
│ ├── radix/ # Radix tree implementation
│ ├── resource/ # Resource system core (v2 architecture CRUD framework)
│ ├── resourcehcl/ # HCL parsing for resource API
│ ├── storage/ # Storage backends: in-memory and Raft-backed
│ │ ├── inmem/ # In-memory storage (tests, dev mode)
│ │ └── raft/ # Raft FSM storage backend
│ ├── testing/ # Internal test helpers (golden files, error helpers)
│ └── tools/ # protoc plugins (consul-rate-limit, grpc-clone, rpc-glue)
├── ipaddr/ # IP address utilities
├── lib/ # Small reusable utility packages
│ ├── channels/ # Channel helpers
│ ├── decode/ # MapStructure decode helpers
│ ├── file/ # File I/O utilities
│ ├── maps/ # Map utilities
│ ├── mutex/ # Mutex utilities
│ ├── retry/ # Retry/backoff logic
│ ├── routine/ # Goroutine lifecycle management
│ ├── semaphore/ # Semaphore implementation
│ ├── stringslice/ # String slice helpers
│ ├── template/ # Template helpers
│ ├── ttlcache/ # TTL-based cache
│ └── testhelpers/ # Test utilities
├── logging/ # Logging setup (hclog-based)
│ └── monitor/ # Log streaming monitor
├── proto/ # Generated protobuf (private wire formats)
│ └── private/ # Internal proto types
├── proto-public/ # [Sub-module] Public protobuf API definitions
│ ├── pbacl/ # ACL proto types
│ ├── pbconnectca/ # Connect CA proto types
│ ├── pbdataplane/ # Dataplane proto types
│ ├── pbdns/ # DNS proto types
│ ├── pbmulticluster/ # Multi-cluster proto types
│ ├── pbresource/ # Resource API proto types (v2)
│ └── pbserverdiscovery/ # Server discovery proto types
├── sdk/ # [Sub-module] Lightweight SDK (safe for external use)
│ ├── freeport/ # Free port finder (test helper)
│ ├── iptables/ # iptables manipulation for transparent proxy
│ └── testutil/ # Test server helpers
├── sentinel/ # Sentinel policy evaluation stubs (enterprise feature hook)
├── service_os/ # OS-specific service management (Windows service support)
├── snapshot/ # Snapshot serialization/deserialization
├── test/ # Test fixtures, certs, and integration test infra
│ └── integration/ # Integration test containers (consul-container framework)
├── test-integ/ # End-to-end integration tests
│ ├── connect/ # Connect/service-mesh E2E tests
│ ├── peering_commontopo/ # Peering topology E2E tests
│ └── upgrade/ # Upgrade compatibility E2E tests
├── testing/
│ └── deployer/ # [Sub-module] Topology deployment framework for E2E tests
├── testrpc/ # Test RPC helper (starts test servers)
├── tlsutil/ # TLS configuration and certificate utilities
├── tools/
│ └── internal-grpc-proxy/ # Internal gRPC proxy tool (main.go)
├── troubleshoot/ # [Sub-module] Troubleshooting commands and proxy validation
├── types/ # Shared fundamental types (NodeID, ServiceID, etc.)
├── ui/ # Ember.js web UI (non-Go)
└── version/ # Version info (injected via ldflags)Entry points#
| Binary | File | Description |
|---|---|---|
consul | main.go | The single unified binary — delegates to command.RegisteredCommands() via mitchellh/cli. Acts as agent, server, CLI tool, and dev-mode server. |
internal-grpc-proxy | tools/internal-grpc-proxy/main.go | Internal development/debugging tool: transparent gRPC proxy for inspecting inter-server traffic. |
The root main.go is intentionally thin: it sets up a mitchellh/cli.CLI instance, registers all commands from command/registry.go, and dispatches. No framework magic; the entire command surface is visible in one file.
Package organization#
Internal packages (
internal/):internal/controller— Resource controller runtime for the v2 architecture; manages reconciliation loops for resource typesinternal/storage— Storage abstraction with in-memory and Raft-backed backendsinternal/resource— Resource CRUD framework (v2 API system)internal/multicluster— Multi-cluster federation resource typesinternal/gossip— Wrappers over HashiCorp’s Serf/memberlist gossip librariesinternal/go-sso— Vendored OIDC/SSO auth library (inlined to avoid external dependency)internal/tools— Custom protoc code generators (not runtime packages)
Public packages (
pkg/-equivalent; exported but nopkg/directory):api/— Full HTTP client library for Consul (its own Go module; widely consumed externally)sdk/— Minimal SDK with iptables, test utilities, and port helpers (own module)proto-public/— Public protobuf-generated types for gRPC APIs (own module)envoyextensions/— Envoy xDS extension framework (own module)connect/— Service mesh types and proxy protocol helpersacl/— ACL policy engine (imported by many packages)tlsutil/— TLS configuration helperslib/— Collection of small utility packages (retry, routine, semaphore, etc.)
Layering: Consul uses a domain-driven, not a clean/hexagonal architecture. The layering is roughly:
lib/,types/,ipaddr/— pure utilities, no consul depsacl/,tlsutil/,logging/— infrastructure concernsagent/structs/— shared data model (imported by almost everything)agent/consul/— server core (Raft FSM, state store, RPC handlers)agent/— agent runtime (health checks, DNS, cache, proxycfg)command/— CLI wrappers around agent functionality
There is notable layering violation:
agent/structs/acts as a “god package” for shared types, creating a diamond dependency across the codebase.
Build system#
- Build tool: GNU Make (
Makefileat root), withgo buildas the underlying compiler. Usesldflagsto injectGitCommitandBuildDatevia theversionpackage. - Key targets:
make dev— build development binary for the current platformmake test— run unit tests viagotestsummake generate— regenerate protobuf types (usesbuf, custom protoc plugins)make lint— run golangci-lintmake docker— build Docker images
- Docker: Yes, multi-stage (
Dockerfileat root with four named targets):official— downloads pre-built release from releases.hashicorp.com (Alpine-based)default— uses CI-built binary copied fromdist/(Alpine-based)dev— for local developmentubi— Red Hat Universal Base Image (OpenShift-compatible)
- Ports exposed in Docker: 8300 (server RPC), 8301/8302 (Serf LAN/WAN gossip), 8500 (HTTP API), 8600 (DNS)
- Proto tooling:
buffor proto linting/generation; custom protoc plugins ininternal/tools/for rate-limit annotations, gRPC clone generation, and RPC glue
Notable structural decisions#
Root-level
main.goinstead ofcmd/: Consul predates thecmd/-per-binary convention being common. The single rootmain.goreflects that there is truly one binary doing everything — and it’s never needed to change.Multi-module monorepo with
replacedirectives:api/,sdk/,proto-public/,envoyextensions/, andtroubleshoot/are independent Go modules pinned viareplacein the rootgo.mod. This allows consumers to import justconsul/apiwithout the full server dependency tree — a practical split for the most widely-used client library.Two parallel architectures (“v1” and “v2”): The
internal/resource/,internal/controller/, andinternal/storage/packages represent an emerging resource-oriented v2 architecture (documented indocs/v2-architecture/). The existingagent/consul/(Raft FSM + state store + RPC handlers) is the v1 architecture. Both coexist in the same binary, indicating an active long-running migration.agent/structs/as the universal data model: Rather than thin DTOs at API boundaries, Consul centralizes nearly all wire and storage types inagent/structs/. This creates high coupling but eliminates translation layers — a pragmatic choice for a project that evolved incrementally.Inlined vendor (
internal/go-sso/): The OIDC/SSO library is vendored insideinternal/go-sso/rather than as a go.sum dependency. This is unusual and suggests it diverged significantly from any upstream, or needed private modifications.Dedicated integration test infrastructure:
testing/deployer/(its own Go module) is a full topology deployment framework for spinning up multi-node Consul clusters in containers, used bytest-integ/E2E tests. This is more sophisticated than typical integration test setups and reflects the operational complexity of testing a distributed system.Custom protoc generators as first-class tools:
internal/tools/protoc-gen-consul-rate-limitandprotoc-gen-grpc-cloneare purpose-built code generators checked into the repo, not third-party tools. This allows rate limiting and clone semantics to be expressed in.protoannotations rather than handwritten per-RPC.