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):

  1. geth looks for the plugin distribution file after reading the plugin definition from settings
  2. geth verifies the plugin distribution file integrity
  3. geth generates a self-signed certificate (aka client certificate)
  4. geth spawns the plugin with the client certificate
  5. The plugin imports the client certificate and generates a self-signed server certificate for its RPC server
  6. The plugin includes the RPC server certificate in the handshake
  7. geth imports the plugin RPC server certificate
  8. geth 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 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.