Skip to main content

wash_runtime/plugin/
mod.rs

1//! Plugin system for extending host capabilities.
2//!
3//! This module provides the plugin framework that allows the wasmcloud host
4//! to support different WASI interfaces and capabilities. Plugins implement
5//! specific functionality that components can use through standard interfaces.
6//!
7//! # Plugin Architecture
8//!
9//! Plugins are Rust types that implement the [`HostPlugin`] trait. They:
10//! - Declare which WIT interfaces they provide via [`HostPlugin::world`]
11//! - Bind to components that need their capabilities via [`HostPlugin::bind_component`]
12//! - Can participate in workload lifecycle events
13//! - Are automatically linked into the wasmtime runtime
14//!
15//! # Built-in Plugins
16//!
17//! The crate provides several built-in plugins for common WASI interfaces:
18//! - [`wasi_http`] - HTTP server capabilities (`wasi:http/incoming-handler`)
19//! - [`wasi_config`] - Runtime configuration (`wasi:config/runtime`)
20//! - [`wasi_blobstore`] - Object storage (`wasi:blobstore`)
21//! - [`wasi_keyvalue`] - Key-value storage (`wasi:keyvalue`)
22//! - [`wasi_logging`] - Structured logging (`wasi:logging`)
23
24use crate::{
25    engine::workload::{ResolvedWorkload, UnresolvedWorkload, WorkloadComponent},
26    wit::WitWorld,
27};
28
29#[cfg(feature = "wasi-http")]
30pub mod wasi_http;
31
32#[cfg(feature = "wasi-config")]
33pub mod wasi_config;
34
35#[cfg(feature = "wasi-blobstore")]
36pub mod wasi_blobstore;
37
38#[cfg(feature = "wasi-keyvalue")]
39pub mod wasi_keyvalue;
40
41#[cfg(feature = "wasi-logging")]
42pub mod wasi_logging;
43
44// TODO: Try to get rid of the `async-trait` usage if possible, and set up the ID as an associated constant.
45/// The [`HostPlugin`] trait provides an interface for implementing built-in plugins for the host.
46/// A plugin is primarily responsible for implementing a specific [`WitWorld`] as a collection of
47/// imports and exports that will be directly linked to the workload's [`wasmtime::component::Linker`].
48///
49/// For example, the runtime doesn't implement `wasi:keyvalue`, but it's a key capability for many component
50/// applications. This crate provides a [`wasi_keyvalue::WasiKeyvalue`] built-in that persists key-value data
51/// in-memory and implements the component imports of `wasi:keyvalue` atomics, batch and store.
52///
53/// You can supply your own [`HostPlugin`] implementations to the [`crate::host::HostBuilder::with_plugin`] function.
54#[async_trait::async_trait]
55pub trait HostPlugin: std::any::Any + Send + Sync + 'static {
56    /// Returns the unique identifier for this plugin.
57    ///
58    /// This ID must be unique across all plugins registered with a host.
59    /// It's used to retrieve plugin instances and avoid conflicts.
60    ///
61    /// # Returns
62    /// A static string slice containing the plugin's unique identifier.
63    fn id(&self) -> &'static str;
64
65    /// Returns the WIT interfaces that this plugin provides.
66    ///
67    /// The returned `WitWorld` contains the imports and exports that this plugin
68    /// implements. The plugin's `bind_component` method will only be called if
69    /// a workload requires one of these interfaces.
70    ///
71    /// # Returns
72    /// A `WitWorld` containing the plugin's imports and exports.
73    fn world(&self) -> WitWorld;
74
75    /// Called when the plugin is started during host initialization.
76    ///
77    /// This method allows plugins to perform any necessary setup before
78    /// accepting workloads. The default implementation does nothing.
79    ///
80    /// # Returns
81    /// Ok if the plugin started successfully.
82    ///
83    /// # Errors
84    /// Returns an error if the plugin fails to initialize, which will
85    /// prevent the host from starting.
86    async fn start(&self) -> anyhow::Result<()> {
87        Ok(())
88    }
89
90    /// Called when a workload is binding to this plugin.
91    ///
92    /// This method is invoked when a workload is in the process of being bound to the plugin,
93    /// allowing the plugin to perform any necessary setup or validation before the binding is finalized.
94    /// The default implementation does nothing.
95    ///
96    /// # Arguments
97    /// * `workload` - The unresolved workload that is being bound.
98    /// * `interfaces` - The set of WIT interfaces that the workload requires from this plugin.
99    ///
100    /// # Returns
101    /// Ok if the binding preparation succeeded.
102    ///
103    /// # Errors
104    /// Returns an error if the plugin cannot support the requested binding.
105    async fn on_workload_bind(
106        &self,
107        _workload: &UnresolvedWorkload,
108        _interfaces: std::collections::HashSet<crate::wit::WitInterface>,
109    ) -> anyhow::Result<()> {
110        Ok(())
111    }
112
113    /// Called when a [`WorkloadComponent`] is being bound to this plugin.
114    ///
115    /// This method is called when a workload requires interfaces that this
116    /// plugin provides. The plugin should configure the component's linker
117    /// with the necessary implementations.
118    ///
119    /// # Arguments
120    /// * `component` - The workload component to bind to this plugin
121    /// * `interfaces` - The specific WIT interfaces the component requires
122    ///
123    /// # Returns
124    /// Ok if binding succeeded.
125    ///
126    /// # Errors
127    /// Returns an error if the plugin cannot bind to the component.
128    async fn on_component_bind(
129        &self,
130        _component: &mut WorkloadComponent,
131        _interfaces: std::collections::HashSet<crate::wit::WitInterface>,
132    ) -> anyhow::Result<()> {
133        Ok(())
134    }
135
136    /// Called when a workload has been fully resolved and is ready for use.
137    ///
138    /// This optional callback allows plugins to perform actions after a workload
139    /// has been successfully bound and resolved. The default implementation
140    /// does nothing.
141    ///
142    /// # Arguments
143    /// * `workload` - The fully resolved workload
144    /// * `component_id` - The ID of the specific component within the workload
145    ///
146    /// # Returns
147    /// Ok if the callback completed successfully.
148    ///
149    /// # Errors
150    /// Returns an error if the plugin fails to handle the resolved workload.
151    async fn on_workload_resolved(
152        &self,
153        _workload: &ResolvedWorkload,
154        _component_id: &str,
155    ) -> anyhow::Result<()> {
156        Ok(())
157    }
158
159    /// Called when a workload is being stopped or unbound from this plugin.
160    ///
161    /// This method allows plugins to clean up any resources associated with
162    /// the workload. The default implementation does nothing.
163    ///
164    /// # Arguments
165    /// * `workload` - The workload being unbound
166    /// * `interfaces` - The interfaces that were bound
167    ///
168    /// # Returns
169    /// Ok if unbinding succeeded.
170    ///
171    /// # Errors
172    /// Returns an error if cleanup fails.
173    async fn on_workload_unbind(
174        &self,
175        _workload: &ResolvedWorkload,
176        _interfaces: std::collections::HashSet<crate::wit::WitInterface>,
177    ) -> anyhow::Result<()> {
178        Ok(())
179    }
180
181    /// Called when the plugin is being stopped during host shutdown.
182    ///
183    /// This method allows plugins to perform cleanup before the host stops.
184    /// The default implementation does nothing.
185    ///
186    /// # Returns
187    /// Ok if the plugin stopped successfully.
188    ///
189    /// # Errors
190    /// Returns an error if cleanup fails (errors are logged but don't prevent shutdown).
191    async fn stop(&self) -> anyhow::Result<()> {
192        Ok(())
193    }
194}