Skip to content

Components

A Component is a portable WebAssembly (WASM) building block that implements the Greentic WIT interface. Components are:

  • Sandboxed - Execute in isolated WASM environment
  • Portable - Run on any platform with Wasmtime
  • Composable - Combine to build complex workflows
  • Language-agnostic - Write in Rust, Go, or any WASM-compatible language
TypePurposeExample
NodeFlow processing stepLLM caller, template renderer
ProviderExternal service bridgeTelegram, Slack, SendGrid
ToolMCP tool implementationDatabase query, API call
OperatorMessage transformationButton handling, card rendering

Use the component authoring CLI:

Terminal window
# Create new component project
greentic-component new my-processor
cd my-processor

This generates:

my-processor/
├── Cargo.toml
├── src/
│ └── lib.rs
├── wit/
│ └── component.wit
└── build.sh
Cargo.toml
[package]
name = "my-processor"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.53"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[profile.release]
opt-level = "s"
lto = true

Define your component’s interface:

wit/component.wit
package greentic:my-processor;
interface types {
record input {
message: string,
context: option<string>,
}
record output {
result: string,
success: bool,
}
}
world processor {
import types;
export process: func(input: types.input) -> types.output;
}
src/lib.rs
use wit_bindgen::generate;
generate!({
world: "processor",
path: "wit",
});
struct MyProcessor;
impl Guest for MyProcessor {
fn process(input: Input) -> Output {
// Your processing logic here
let result = format!("Processed: {}", input.message);
Output {
result,
success: true,
}
}
}
export!(MyProcessor);
target/wasm32-wasip2/release/my_processor.wasm
# Build for WASM target
cargo build --target wasm32-wasip2 --release

Components can perform async operations using WASI interfaces:

use wit_bindgen::generate;
generate!({
world: "async-processor",
path: "wit",
async: true,
});
impl Guest for AsyncProcessor {
async fn process(input: Input) -> Output {
// Async HTTP call
let response = http_fetch(&input.url).await;
Output {
result: response.body,
success: response.status == 200,
}
}
}

Access session state within components:

impl Guest for StatefulProcessor {
fn process(input: Input, state: &mut State) -> Output {
// Read from state
let counter = state.get("counter").unwrap_or(0);
// Update state
state.set("counter", counter + 1);
Output {
result: format!("Processed {} times", counter + 1),
success: true,
}
}
}

Use the Result type for error handling:

impl Guest for SafeProcessor {
fn process(input: Input) -> Result<Output, Error> {
if input.message.is_empty() {
return Err(Error::InvalidInput("Message cannot be empty".into()));
}
Ok(Output {
result: process_message(&input.message)?,
success: true,
})
}
}

Greentic provides several built-in components:

Call OpenAI-compatible LLMs:

- id: analyze
type: llm
config:
model: "gpt-4"
system_prompt: "You are a helpful assistant."
prompt: "{{message}}"

Render Handlebars templates:

- id: format
type: template
config:
template: "Hello, {{name}}! Your order #{{order_id}} is ready."

Execute Rhai scripts:

- id: calculate
type: script
config:
script: |
let total = 0;
for item in items {
total += item.price * item.quantity;
}
total

Render and validate Adaptive Cards:

- id: show_card
type: adaptive-card
config:
card: "cards/welcome.json"
data:
user_name: "{{user_name}}"
src/lib.rs
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_process() {
let input = Input {
message: "Hello".into(),
context: None,
};
let output = MyProcessor::process(input);
assert!(output.success);
assert!(output.result.contains("Processed"));
}
}
Terminal window
# Run with test harness
greentic-component test ./my-processor
# Test with sample input
echo '{"message": "test"}' | greentic-component run ./my-processor.wasm
  1. Keep components focused - Single responsibility
  2. Handle all errors - Never panic in production
  3. Minimize dependencies - Smaller WASM binaries
  4. Use strong types - Leverage WIT for type safety
  5. Document interfaces - Clear WIT definitions
  6. Test thoroughly - Unit and integration tests
  7. Optimize size - Use LTO and size optimization
Terminal window
# Inspect component exports
wasm-tools component wit ./my-processor.wasm
# Validate component
wasm-tools validate ./my-processor.wasm

Use the WASI logging interface:

use greentic_interfaces::log;
impl Guest for DebugProcessor {
fn process(input: Input) -> Output {
log::debug(&format!("Processing: {:?}", input));
// ... processing ...
log::info("Processing complete");
output
}
}