Brand Host
plant_runner provides the host-side command channel between a running
WASM brand component and the forge runtime. These types have no dependency on
wasmtime or any WIT bindings; they are the protocol boundary that plant_world
uses to bridge its brand component hosting into the generic runner.
BrandHostCommand
pub enum BrandHostCommand {
EnqueuePatch { hob_id: String, payload: String },
ClockStep,
ClockPause,
ClockResume,
ClockStatus,
}
| Variant | Triggered by |
|---|---|
EnqueuePatch |
Brand component submits a state mutation for a specific hob |
ClockStep |
Brand component requests a single frame advance |
ClockPause |
Brand component requests the clock be paused |
ClockResume |
Brand component requests the clock be resumed |
ClockStatus |
Brand component queries current clock state |
HostCommandSink
pub trait HostCommandSink: Send + Sync {
fn submit(&self, cmd: BrandHostCommand) -> Result<(), String>;
}
HostCommandSink is the synchronous submission API exposed to WIT host import
implementations. It is intentionally synchronous: the implementation is expected
to enqueue work onto the async runtime (e.g. via tokio::sync::mpsc) rather
than blocking the calling thread.
Architecture
WASM brand component
|
| (WIT host import call)
v
plant_world::brand_plugin::loader
| (calls submit())
v
HostCommandSink implementation (in plant_world or plant_runner host)
| (enqueues via mpsc)
v
Async forge runner task
| (dispatches to FrameManager / clock)
v
FrameManager / ClockState
The WIT bindgen block, BrandHostState, and BrandComponentService remain in
plant_world::brand_plugin because they are bound to the
plant:brand/plugin@0.1.0 WIT world. BrandHostCommand and HostCommandSink
live in plant_runner because they represent the generic command surface
that any forged kind's WASM host would need.
Usage
Implement HostCommandSink in the brand plugin layer:
struct MyCommandSink {
tx: mpsc::Sender<BrandHostCommand>,
}
impl HostCommandSink for MyCommandSink {
fn submit(&self, cmd: BrandHostCommand) -> Result<(), String> {
self.tx.try_send(cmd).map_err(|e| e.to_string())
}
}
Supply the sink to FrameManager so it can route brand commands:
frame_manager.set_command_sink(Arc::new(MyCommandSink { tx }));
Relationship to plant_world
plant_world::brand_plugin::loader imports these types via:
pub use plant_runner::{BrandHostCommand, HostCommandSink};
No duplicate definitions exist in world. The WIT-specific binding code around
them remains in world because it references plant:brand/plugin@0.1.0 types
that are not generic.
Related Documentation
- Clock Reference — The clock actions that brand components can trigger
- Frame Manager Reference — How commands reach
FrameManager - Brand Plugins Architecture — Full WASM component model integration