Expand description
§Drasi Plugin SDK
The Drasi Plugin SDK provides the traits, types, and utilities needed to build plugins for the Drasi Server. Plugins can be compiled directly into the server binary (static linking) or built as shared libraries for dynamic loading.
§Quick Start
use drasi_plugin_sdk::prelude::*;
// 1. Define your configuration DTO with OpenAPI schema support
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct MySourceConfigDto {
/// The hostname to connect to
#[schema(value_type = ConfigValueString)]
pub host: ConfigValue<String>,
/// The port number
#[schema(value_type = ConfigValueU16)]
pub port: ConfigValue<u16>,
/// Optional timeout in milliseconds
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<ConfigValueU32>)]
pub timeout_ms: Option<ConfigValue<u32>>,
}
// 2. Implement the appropriate descriptor trait
pub struct MySourceDescriptor;
#[async_trait]
impl SourcePluginDescriptor for MySourceDescriptor {
fn kind(&self) -> &str { "my-source" }
fn config_version(&self) -> &str { "1.0.0" }
fn config_schema_json(&self) -> String {
let schema = <MySourceConfigDto as utoipa::ToSchema>::schema();
serde_json::to_string(&schema).unwrap()
}
async fn create_source(
&self,
id: &str,
config_json: &serde_json::Value,
auto_start: bool,
) -> anyhow::Result<Box<dyn drasi_lib::sources::Source>> {
let dto: MySourceConfigDto = serde_json::from_value(config_json.clone())?;
let mapper = DtoMapper::new();
let host = mapper.resolve_string(&dto.host)?;
let port = mapper.resolve_typed(&dto.port)?;
// Build and return your source implementation...
todo!()
}
}
// 3. Create a plugin registration
pub fn register() -> PluginRegistration {
PluginRegistration::new()
.with_source(Box::new(MySourceDescriptor))
}§Static vs. Dynamic Plugins
Plugins can be integrated with Drasi Server in two ways:
§Static Linking
Compile the plugin directly into the server binary. Create a
PluginRegistration and pass its descriptors
to the server’s plugin registry at startup. This is the simplest approach and
is shown in the Quick Start above.
§Dynamic Loading
Build the plugin as a shared library (cdylib) that the server loads at runtime
from a plugins directory. This allows deploying new plugins without recompiling
the server. See Creating a Dynamic Plugin below
for the full workflow.
§Creating a Dynamic Plugin
Dynamic plugins are compiled as shared libraries (.so on Linux, .dylib on
macOS, .dll on Windows) and placed in the server’s plugins directory. The server
discovers and loads them automatically at startup.
§Step 1: Set up the crate
In your plugin’s Cargo.toml, set the crate type to cdylib:
[lib]
crate-type = ["cdylib"]
[dependencies]
drasi-plugin-sdk = "..." # Must match the server's version exactly
drasi-lib = "..."§Step 2: Implement descriptor(s)
Implement SourcePluginDescriptor,
ReactionPluginDescriptor, and/or
BootstrapPluginDescriptor for your
plugin. See the descriptor module docs for the full trait requirements.
§Step 3: Export the entry point
Every dynamic plugin shared library must export a C function named
drasi_plugin_init that returns a heap-allocated
PluginRegistration via raw pointer:
use drasi_plugin_sdk::prelude::*;
#[no_mangle]
pub extern "C" fn drasi_plugin_init() -> *mut PluginRegistration {
let registration = PluginRegistration::new()
.with_source(Box::new(MySourceDescriptor))
.with_reaction(Box::new(MyReactionDescriptor));
Box::into_raw(Box::new(registration))
}Important details:
- The function must be
#[no_mangle]andextern "C"so the server can find it via the C ABI. - The
PluginRegistrationmust be heap-allocated withBox::newand returned as a raw pointer viaBox::into_raw. The server takes ownership by callingBox::from_raw. - The
PluginRegistration::new()constructor automatically embeds theSDK_VERSIONconstant. The server checks this at load time and rejects plugins built with a different SDK version.
§Step 4: Build and deploy
cargo build --release
# Copy the shared library to the server's plugins directory
cp target/release/libmy_plugin.so /path/to/plugins/§Compatibility Requirements
Both the plugin and the server must be compiled with:
- The same Rust toolchain version (the Rust ABI is not stable across versions).
- The same
drasi-plugin-sdkversion. The server comparesSDK_VERSIONat load time and rejects mismatches.
Failing to meet these requirements will result in the plugin being rejected at load time or, in the worst case, undefined behavior from ABI incompatibility.
§Modules
config_value— TheConfigValue<T>enum for configuration fields that support static values, environment variables, and secrets.resolver— Value resolvers that convert config references to actual values.mapper— TheDtoMapperservice andConfigMappertrait for DTO-to-domain conversions.descriptor— Plugin descriptor traits (SourcePluginDescriptor,ReactionPluginDescriptor,BootstrapPluginDescriptor).registration— ThePluginRegistrationstruct returned by plugin entry points.prelude— Convenience re-exports for plugin authors.
§Configuration Values
Plugin DTOs use ConfigValue<T> for fields that may
be provided as static values, environment variable references, or secret references.
See the config_value module for the full documentation and supported formats.
§OpenAPI Schema Generation
Each plugin provides its configuration schema as a JSON-serialized utoipa Schema.
The server deserializes these schemas and assembles them into the OpenAPI specification.
This approach preserves strongly-typed OpenAPI documentation while keeping schema
ownership with the plugins.
§DTO Versioning
Each plugin independently versions its configuration DTO using semver. The server
tracks config versions and can reject incompatible plugins. See the descriptor
module docs for versioning rules.
Re-exports§
pub use config_value::ConfigValue;pub use descriptor::BootstrapPluginDescriptor;pub use descriptor::ReactionPluginDescriptor;pub use descriptor::SourcePluginDescriptor;pub use mapper::ConfigMapper;pub use mapper::DtoMapper;pub use mapper::MappingError;pub use registration::PluginRegistration;pub use registration::SDK_VERSION;pub use resolver::register_secret_resolver;pub use resolver::ResolverError;
Modules§
- config_
value - Configuration value types that support static values or dynamic references.
- descriptor
- Plugin descriptor traits that define how plugins advertise their capabilities.
- ffi
- FFI layer for cdylib dynamic plugin loading.
- mapper
- DTO-to-domain model mapping service with value resolution.
- prelude
- Convenience re-exports for plugin authors.
- registration
- Plugin registration and discovery.
- resolver
- Value resolvers for
ConfigValuereference types.
Macros§
- export_
plugin - Export dynamic plugin entry points with FFI vtables.