title: Plugin Development - Pluggable Architecture - Smilo
We leverage HashiCorp's go-plugin
to enable our plugin-based architecture using gRPC.
We recommend reading the go-plugin
gRPC examples.
Some advanced topics which are not available in the go-plugin
documentation will be covered here.
Life Cycle
A plugin is started as a separate process and communicates with the Smilo client host process via gRPC service interfaces.
This is done over a mutually-authenticated TLS connection on the local machine. The implementation is done inside go-plugin
library. Usage is simplest when developing plugins in Golang. For plugins written in other languages, plugin authors need to have
an understanding of the following lifecycle (see Advanced topics for non-Go plugins for more info):
geth
looks for the plugin distribution file after reading the plugin definition from settingsgeth
verifies the plugin distribution file integritygeth
generates a self-signed certificate (aka client certificate)geth
spawns the plugin with the client certificate- The plugin imports the client certificate and generates a self-signed server certificate for its RPC server
- The plugin includes the RPC server certificate in the handshake
geth
imports the plugin RPC server certificategeth
and the plugin communicate via RPC over TLS using mutual TLS
Each plugin must implement the PluginInitializer
gRPC service interface.
After the plugin process is successfully started and connection with the Smilo client is successfully established,
Smilo client invokes Init()
gRPC method in order to initialize the plugin with configuration data
read from the plugin definition's config
field in settings file.
Distribution
File format
Plugin distribution file must be a ZIP file. File name format is <name>-<version>.zip
.
<name>
and <version>
must be the same as the values defined in the PluginDefinition
object in the settings file.
Metadata
A plugin metadata file plugin-meta.json
must be included in the distribution ZIP file.
plugin-meta.json
contains a valid JSON object which has a flat structure with key value pairs.
Although the JSON object can include any desired information. There are mandatory key value pairs which must be present.
{
"name": string,
"version": string,
"entrypoint": string,
"parameters": array(string),
...
}
Fields | Description |
---|---|
name | (Required) Name of the plugin |
version | (Required) Version of the plugin |
entrypoint | (Required) Command to execute the plugin process |
parameters | (Optional) Command parameters to be passed to the plugin process |
E.g.:
{
"name": "Smilo-plugin-helloWorld",
"version": "1.0.0",
"entrypoint": "helloWorldPlugin"
}
Advanced topics for non-Go plugins
Writing non-Go plugins is well-documented in go-plugin
Github.
Some additional advanced topics are described here.
Magic Cookie
Magic Cookie key and value are used as a very basic verification that a plugin is intended to be launched. This is not a security measure, just a UX feature.
Magic Cookie key and value are injected as an environment variable while executing the plugin process.
QUORUM_PLUGIN_MAGIC_COOKIE="CB9F51969613126D93468868990F77A8470EB9177503C5A38D437FEFF7786E0941152E05C06A9A3313391059132A7F9CED86C0783FE63A8B38F01623C8257664"
The plugin and the Smilo client's magic cookies are compared. If they are equal then the plugin is loaded. If they are not equal, the plugin should show human-friendly output.
Mutual TLS Authentication
The Smilo client requires the plugin to authenticate and secure the connection via mutual TLS.
PLUGIN_CLIENT_CERT
environment variable is populated with Smilo Client certificate (in PEM format).
A plugin would need to include this certificate to its trusted certificate pool, then
generate a self-signed certificate and append the base64-encoded value of the certificate (in DER format)
in the handshake message.
{!./PluggableArchitecture/Plugins/init_interface.md!}
Examples
Please visit Overview page for a built-in HelloWorld plugin example.