This crate defines the API between reaction's core and plugins.
Plugins must be written in Rust, for now.
This documentation assumes the reader has some knowledge of Rust. However, if you find that something is unclear, don't hesitate to ask for help, even if you're new to Rust.
To implement a plugin, one has to provide an implementation of [PluginInfo], that provides
the entrypoint for a plugin.
It permits to define 0 to n custom stream and action types.
Note on reaction-plugin API stability
This is the v1 of reaction's plugin interface.
It's quite efficient and complete, but it has the big drawback of being Rust-only and [tokio]-only.
In the future, I'd like to define a language-agnostic interface, which will be a major breaking change in the API. However, I'll try my best to reduce the necessary code changes for plugins that use this v1.
Naming & calling conventions
Your plugin should be named reaction-plugin-$NAME, eg. reaction-plugin-postgresql.
It will be invoked with one positional argument "serve".
This can be useful if you want to provide CLI functionnality to your users, so you can distinguish between a human user and reaction.
State directory
It will be executed in its own directory, in which it should have write access.
The directory is $reaction_state_directory/plugin_data/$NAME.
reaction's state_directory
defaults to its working directory, which is /var/lib/reaction in most setups.
So your plugin directory should most often be /var/lib/reaction/plugin_data/$NAME,
but the plugin shouldn't expect that and use the current working directory instead.
Communication
Communication between the plugin and reaction is based on [remoc], which permits to multiplex channels and remote objects/functions/trait
calls over a single transport channel.
The channels read and write channels are stdin and stdout, so you shouldn't use them for something else.
[remoc] builds upon [tokio], so you'll need to use tokio too.
Errors
Errors during:
- config loading in [
PluginInfo::load_config] - startup in [
PluginInfo::start]
should be returned to reaction by the function's return value, permitting reaction to abort startup.
During normal runtime, after the plugin has loaded its config and started, and before reaction is quitting, there is no rusty way to send errors to reaction. Then errors can be printed to stderr. They'll be captured line by line and re-printed by reaction, with the plugin name prepended.
A line can start with DEBUG , INFO , WARN , ERROR .
If it starts with none of the above, the line is assumed to be an error.
Example: Those lines:
WARN This is an official warning from the plugin
Freeeee errrooooorrr
Will become:
WARN plugin test: This is an official warning from the plugin
ERROR plugin test: Freeeee errrooooorrr
Plugins should not exit when there is an error: reaction quits only when told to do so, or if all its streams exit, and won't retry starting a failing plugin or stream. Please only exit if you're in a 100% failing state. It's considered better to continue operating in a degraded state than exiting.
Getting started
If you don't have Rust already installed, follow their Getting Started documentation to get rust build tools and learn about editor support.
Then create a new repository with cargo:
Add required dependencies:
Replace src/main.rs with those contents:
use reaction_plugin::PluginInfo;
#[tokio::main]
async fn main() {
let plugin = MyPlugin::default();
reaction_plugin::main_loop(plugin).await;
}
#[derive(Default)]
struct MyPlugin {}
impl PluginInfo for MyPlugin {
// ...
}
Your IDE should now propose to implement missing members of the [PluginInfo] trait.
Your journey starts!
Examples
Core plugins can be found here: https://framagit.org/ppom/reaction/-/tree/main/plugins.
- The "virtual" plugin is the simplest and can serve as a good complete example that links custom stream types and custom action types.
- The "ipset" plugin is a good example of an action-only plugin.