go-plugin

HashiCorp's Go library for building plugin systems over RPC or gRPC, powering Terraform, Vault, and Nomad.

Library
Go
vv1.8.0
6,084stars
Mozilla Public License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
82/100Excellent
Development Activity84
Maintenance60
Community84
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
88/100Excellent
Architecture85
Code Quality85
Innovation82
Learning Curve100

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/Server API 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/GRPCBroker for opening additional multiplexed connections, enabling bidirectional calls and complex argument types like io.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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search