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}