How Core and Plugins Communicate
The communication channel between Core and Plugins is gRPC (Google Remote Procedure Call) — a high-performance, binary protocol built on top of HTTP/2. gRPC uses Protocol Buffers (protobuf) to define the message format, which means both sides agree on a strict schema for every call. This is what makes the plugin system stable across versions.
The full lifecycle of a plugin connection looks like this:
- Spawn: Core starts the plugin binary as a child process.
- Handshake: The plugin writes a magic handshake string to
stdout— something like1|5|tcp|127.0.0.1:12345|grpc. This tells Core: "I am a Terraform plugin, protocol version 5, listening on localhost port 12345 via gRPC." - Connect: Core opens a gRPC connection to that local address.
- RPC Calls: Core makes structured remote procedure calls to the plugin:
GetProviderSchema— "Tell me what resources and data sources you support."ValidateResourceConfig— "The user wrote this HCL. Is it valid?"PlanResourceChange— "Here is the old state and the new config. What will change?"ApplyResourceChange— "Execute the change and return the new state."
This architecture is powered by HashiCorp's open-source go-plugin library. Its key properties are:
- Crash isolation: If a plugin panics, Core catches the process exit and reports a clean error instead of crashing itself.
- Language agnosticism: Any language that can implement the gRPC interface can be a provider. In practice, Go is the de-facto standard because the SDK and Framework are written in Go.
- Versioned protocol: The handshake includes a protocol version number, allowing Core and plugins to negotiate compatibility.
The go-plugin library also handles mTLS (mutual TLS) automatically for the local gRPC connection, preventing other processes on the same machine from injecting malicious responses into the plugin channel. This is a subtle but important security detail — even though the connection is localhost-only, it is still authenticated and encrypted.