go-plugin
HashiCorp's Go library for building plugin systems over RPC or gRPC, powering Terraform, Vault, and Nomad.
Repository Health
Technical Analysis
go-plugin is HashiCorp’s Go library for building plugin systems that communicate over RPC, providing the exact mechanism that powers Terraform, Vault, Nomad, Packer, Boundary, and Waypoint. Rather than relying on Go’s native (and limited) shared-library plugin support, it launches each plugin as a separate subprocess and exposes it to the host application as a regular Go interface implementation — the host calls methods as if the plugin lived in the same process, while go-plugin transparently handles process management, connection negotiation, and message passing underneath.
The library supports both net/rpc and gRPC as wire protocols, with gRPC enabling plugins written in any language that supports Protocol Buffers. It handles complex scenarios out of the box: bidirectional calls where the plugin can invoke back into the host, automatic mTLS negotiation for authenticated transport, protocol versioning to safely evolve plugin interfaces, and log/stdio syncing so a plugin subprocess’s output flows cleanly into the host’s own logging and terminal. Having run across millions of machines inside HashiCorp’s own tooling for over a decade, it is a proven foundation for any Go application that needs a safe, crash-isolated extension mechanism.
What You Get
- A
Client/ServerAPI that manages the full subprocess lifecycle — launching, health-checking, and killing plugin binaries automatically. - Support for both net/rpc and gRPC transports, with gRPC unlocking plugins written in non-Go languages.
- Automatic mutual TLS (AutoMTLS) so host and plugin authenticate each other without manual certificate wiring.
- A
MuxBroker/GRPCBrokerfor opening additional multiplexed connections, enabling bidirectional calls and complex argument types likeio.Reader/Writer. - Protocol versioning (
VersionedPlugins) to negotiate compatible plugin interfaces across releases. - Reattach support so a host process can restart and resume talking to an already-running plugin.
Common Use Cases
- Building an extensible CLI or infrastructure tool (à la Terraform providers) where third parties ship plugin binaries independently of the core release.
- Isolating unstable or untrusted logic in its own OS process so a panic or crash can’t take down the host application.
- Letting plugins be written in a different language than the host, using the gRPC transport and Protocol Buffers as the contract.
- Allowing a plugin to call back into the host (bidirectional RPC), e.g. a storage plugin invoking a host-provided logging or secrets interface.
Under The Hood
Architecture
go-plugin is organized as a flat package (plugin) split cleanly by concern: client.go owns the host-side subprocess lifecycle (Client.Start/Kill/Client), server.go and plugin.go define the Plugin/GRPCPlugin interfaces plugin authors implement plus Serve, and grpc_broker.go/mux_broker.go implement the connection-multiplexing layer (GRPCBroker/MuxBroker) that lets either side open secondary streams for bidirectional calls and complex argument types. Transport-specific concerns are pushed into internal/ (internal/cmdrunner for process management, internal/grpcmux for multiplexed gRPC dialing, internal/plugin for the broker’s own generated protobuf types) and runner/runner.go defines a small Runner/AttachedRunner interface so a host can swap in its own process supervisor via ClientConfig.RunnerFunc instead of the default OS-process runner. Data flows one way at startup — the client launches a subprocess, reads a single handshake line off stdout to learn the negotiated protocol version and dial address, then hands off to either net/rpc or gRPC — and from then on host and plugin talk as peers over that connection. The core abstraction a consumer depends on is the Plugin/GRPCPlugin interface pair; changing its shape would ripple through every HashiCorp product embedding this library, which is presumably why the maintainers gate any wire-format change behind CoreProtocolVersion and VersionedPlugins rather than touching the interface directly.
Tech Stack
The module targets a recent Go toolchain and depends on a small, deliberately-chosen set of libraries: google.golang.org/grpc and google.golang.org/protobuf for the gRPC transport, github.com/hashicorp/yamux for net/rpc connection multiplexing, github.com/hashicorp/go-hclog for structured logging (including parsing plugin subprocess stderr as JSON log lines), github.com/jhump/protoreflect for dynamic protobuf reflection used by the broker, and github.com/oklog/run for goroutine group lifecycle management. There’s no database, HTTP framework, or CLI layer — the entire surface is a Go library API (plugin.Client, plugin.Serve) built directly on the standard library’s os/exec, net/rpc, and crypto/tls, with protobuf/gRPC code generation driven by a buf configuration. Build tooling is a plain Makefile plus a CI workflow, and Dependabot keeps the dependency set current.
Code Quality
Testing is substantial for an infrastructure library of this size: a double-digit count of _test.go files exist across the root package, a dedicated protobuf-defined test service, and the internal subpackages, exercising handshake negotiation, reattachment, mTLS, and both RPC transports end-to-end using real subprocesses. Error handling is explicit and typed throughout — sentinel errors are exported for callers to check with errors.Is, rather than swallowed or returned as opaque strings. Naming follows consistent Go convention, all public types carry doc comments, and CI runs the test suite on every push alongside a separate workflow that enforces license headers on every file. There’s no static type-checking beyond what the Go compiler already provides, but a CONTRIBUTING guide codifies PR size and testing expectations for external contributors.
What Makes It Unique What go-plugin does that most “plugin system” libraries don’t is treat the plugin boundary as a process boundary by default: rather than dynamic library loading or in-process interface satisfaction, every plugin is a separate OS process reached over RPC, which means a plugin panic literally cannot crash the host and plugins can be written in a different language entirely when using the gRPC transport. The AutoMTLS feature — where client and server each generate a throw-away certificate and exchange the public half during the handshake itself — gives authenticated transport with effectively zero configuration, which is unusual for a library at this level. The broker abstraction that lets a plugin dial back into the host over the same original connection is also a distinctive design choice; most RPC libraries are unidirectional client-to-server and don’t provide a general mechanism for the callee to become a caller. These choices are validated by over a decade of production use inside Vault, where process isolation is a hard security requirement, not just an implementation preference.
Used by 4 apps in this directory
Apache Airflow
Data Engineering
Define, schedule, and monitor complex data workflows as Python code — with a powerful UI, 80+ provider integrations, and battle-tested scalability across thousands of production deployments.
Grafana
Monitoring · Analytics
The open-source observability platform that unifies metrics, logs, and traces from any data source into dynamic, queryable dashboards.
Mattermost
Team Chat · Collaboration · Devops
Open core, self-hosted team collaboration with chat, AI agents, voice calling, and deep DevOps integrations — all under your control.
tau
Devops
Open-source, Git-native platform-as-a-service for building, deploying, and scaling fullstack apps on your own infrastructure with no DevOps required.