Skip to main content

What is a Microkernel?

MoFA’s microkernel is a minimalist core that provides only the essential building blocks for agent systems. Unlike monolithic frameworks that bundle everything together, the microkernel philosophy is:
Core Principle: Keep the kernel simple, stable, and unchanging. All features are extensions built on top.

Microkernel vs Traditional Frameworks

AspectTraditional FrameworkMoFA Microkernel
Core SizeLarge, batteries-includedMinimal, trait-based
StabilityBreaking changes in features affect coreCore remains stable, features evolve independently
ExtensibilityLimited to framework’s designUnlimited via plugins
PerformanceOften compromised for featuresZero-cost abstractions
TestingComplex integration testsSimple unit tests for traits

Microkernel Responsibilities

The mofa-kernel crate contains only four core responsibilities:

1. Lifecycle Management

The MoFAAgent trait defines the core lifecycle:
mofa-kernel/src/agent/core.rs
#[async_trait]
pub trait MoFAAgent: Send + Sync {
    /// Unique agent identifier
    fn id(&self) -> &str;
    
    /// Human-readable name
    fn name(&self) -> &str;
    
    /// Agent capabilities for discovery
    fn capabilities(&self) -> &AgentCapabilities;
    
    /// Initialize agent with context
    async fn initialize(&mut self, ctx: &CoreAgentContext) -> AgentResult<()>;
    
    /// Execute agent logic
    async fn execute(
        &mut self, 
        input: AgentInput, 
        ctx: &CoreAgentContext
    ) -> AgentResult<AgentOutput>;
    
    /// Graceful shutdown
    async fn shutdown(&mut self) -> AgentResult<()>;
    
    /// Current agent state
    fn state(&self) -> AgentState;
    
    /// Handle interrupt signal
    async fn interrupt(&mut self) -> AgentResult<InterruptResult>;
}
Agents follow a strict state machine:State Definitions:
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum AgentState {
    Uninitialized,
    Initializing,
    Ready,
    Running,
    Paused,
    Error(String),
    Shutdown,
}

2. Metadata System

Every agent declares its capabilities for discovery and routing:
mofa-kernel/src/agent/capabilities.rs
pub struct AgentCapabilities {
    /// Capability tags (e.g., "llm", "coding", "search")
    pub tags: Vec<String>,
    
    /// Supported input types
    pub input_types: Vec<InputType>,
    
    /// Supported output types
    pub output_types: Vec<OutputType>,
    
    /// Supports streaming responses
    pub supports_streaming: bool,
    
    /// Supports tool calling
    pub supports_tools: bool,
    
    /// Custom metadata
    pub metadata: HashMap<String, String>,
}
Builder Pattern:
let capabilities = AgentCapabilities::builder()
    .tag("llm")
    .tag("code-generation")
    .input_type(InputType::Text)
    .output_type(OutputType::Text)
    .supports_streaming(true)
    .supports_tools(true)
    .build();
Capabilities enable dynamic agent discovery. The runtime can find suitable agents based on required capabilities without hardcoding.

3. Communication Bus

The kernel provides a minimal message bus abstraction:
mofa-kernel/src/bus/mod.rs
#[async_trait]
pub trait MessageBus: Send + Sync {
    /// Send message to specific agent
    async fn send(&self, to: &str, msg: Message) -> Result<(), BusError>;
    
    /// Broadcast to all agents
    async fn broadcast(&self, msg: Message) -> Result<(), BusError>;
    
    /// Subscribe to topic
    async fn subscribe(&self, topic: &str) -> Result<Receiver<Message>, BusError>;
    
    /// Publish to topic
    async fn publish(&self, topic: &str, msg: Message) -> Result<(), BusError>;
}
Message Structure:
pub struct Message {
    pub id: String,
    pub from: String,
    pub to: Option<String>,
    pub content: MessageContent,
    pub metadata: MessageMetadata,
    pub timestamp: u64,
}

pub enum MessageContent {
    Text(String),
    Binary(Vec<u8>),
    Json(serde_json::Value),
    Event(GlobalEvent),
}

4. Task Scheduling

Priority-based task coordination:
mofa-kernel/src/agent/components/coordinator.rs
pub struct Task {
    pub id: String,
    pub task_type: TaskType,
    pub content: String,
    pub priority: TaskPriority,
    pub target_agent: Option<String>,
    pub timeout_ms: Option<u64>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TaskPriority {
    Low = 0,
    Normal = 1,
    High = 2,
    Urgent = 3,
}

Component Traits

The microkernel defines modular component traits:
mofa-kernel/src/agent/components/tool.rs
#[async_trait]
pub trait Tool: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn metadata(&self) -> &ToolMetadata;
    
    async fn execute(
        &self,
        input: ToolInput,
    ) -> Result<ToolResult, ToolError>;
    
    fn descriptor(&self) -> ToolDescriptor {
        ToolDescriptor {
            name: self.name().to_string(),
            description: self.description().to_string(),
            metadata: self.metadata().clone(),
        }
    }
}
Purpose: Define external capabilities agents can use (API calls, file operations, etc.)
mofa-kernel/src/agent/components/memory.rs
#[async_trait]
pub trait Memory: Send + Sync {
    async fn store(&self, key: String, value: MemoryValue) -> AgentResult<()>;
    async fn retrieve(&self, key: &str) -> AgentResult<Option<MemoryValue>>;
    async fn search(&self, query: &str, limit: usize) -> AgentResult<Vec<MemoryItem>>;
    async fn delete(&self, key: &str) -> AgentResult<bool>;
    
    fn stats(&self) -> MemoryStats;
}
Purpose: Stateful agent memory for conversation history and context
mofa-kernel/src/agent/components/reasoner.rs
#[async_trait]
pub trait Reasoner: Send + Sync {
    async fn reason(
        &self,
        context: &str,
        options: &ReasoningOptions,
    ) -> AgentResult<ReasoningResult>;
    
    fn strategy(&self) -> ReasoningStrategy;
}

pub enum ReasoningStrategy {
    ChainOfThought,
    TreeOfThought,
    ReAct,
    ReflexionReAct,
}
Purpose: LLM-powered reasoning strategies
mofa-kernel/src/agent/components/coordinator.rs
#[async_trait]
pub trait Coordinator: Send + Sync {
    async fn dispatch(
        &self,
        task: Task,
        ctx: &AgentContext,
    ) -> AgentResult<Vec<DispatchResult>>;
    
    async fn aggregate(
        &self,
        results: Vec<AgentOutput>,
    ) -> AgentResult<AgentOutput>;
    
    fn pattern(&self) -> CoordinationPattern;
}
Purpose: Multi-agent collaboration patterns (parallel, sequential, debate, etc.)

Plugin System Integration

The microkernel defines the plugin trait that all extensions implement:
mofa-kernel/src/plugin/mod.rs
#[async_trait]
pub trait AgentPlugin: Send + Sync {
    fn metadata(&self) -> &PluginMetadata;
    fn state(&self) -> PluginState;
    
    // Lifecycle hooks
    async fn load(&mut self, ctx: &PluginContext) -> PluginResult<()>;
    async fn init_plugin(&mut self) -> PluginResult<()>;
    async fn start(&mut self) -> PluginResult<()>;
    async fn pause(&mut self) -> PluginResult<()>;
    async fn resume(&mut self) -> PluginResult<()>;
    async fn stop(&mut self) -> PluginResult<()>;
    async fn unload(&mut self) -> PluginResult<()>;
    
    // Execution
    async fn execute(&mut self, input: String) -> PluginResult<String>;
    
    // Observability
    async fn health_check(&self) -> PluginResult<bool>;
    fn stats(&self) -> HashMap<String, serde_json::Value>;
}
Plugin State Machine:
Important: The microkernel defines the plugin interface but contains zero concrete plugin implementations. All actual plugins live in mofa-plugins or mofa-foundation.

Architecture Rules

MoFA enforces strict architectural boundaries:
1

Kernel defines only traits

mofa-kernel contains zero concrete implementations (except test code). Only interfaces.
2

Foundation provides implementations

mofa-foundation contains all concrete types that implement kernel traits.
3

No circular dependencies

Foundation can depend on kernel. Kernel never depends on foundation.
4

Plugins extend via traits

All plugins implement AgentPlugin and register via the plugin system.
// ✅ CORRECT: Kernel defines trait
// mofa-kernel/src/agent/components/tool.rs
pub trait Tool: Send + Sync {
    fn name(&self) -> &str;
}

// ✅ CORRECT: Foundation implements trait
// mofa-foundation/src/tools/calculator.rs
pub struct Calculator;

impl Tool for Calculator {
    fn name(&self) -> &str { "calculator" }
}

// ❌ WRONG: Kernel should NOT contain implementations
// mofa-kernel/src/agent/components/tool.rs
pub struct CalculatorTool { /* ... */ }
impl Tool for CalculatorTool { /* ... */ }

Error Handling

The microkernel defines a unified error hierarchy:
mofa-kernel/src/error.rs
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum KernelError {
    #[error("Agent error: {0}")]
    Agent(#[from] AgentError),
    
    #[error("Plugin error: {0}")]
    Plugin(#[from] PluginError),
    
    #[error("Bus error: {0}")]
    Bus(#[from] BusError),
    
    #[error("Configuration error: {0}")]
    Config(String),
}

pub type KernelResult<T> = Result<T, KernelError>;
Use thiserror for typed errors, not anyhow. This maintains type safety across API boundaries.

Type Safety Guarantees

The microkernel leverages Rust’s type system for safety: 1. Non-exhaustive enums for forward compatibility:
#[derive(Debug, Clone)]
#[non_exhaustive]  // New variants can be added without breaking changes
pub enum AgentState {
    Idle,
    Running,
    // Future: can add new states here
}
2. Trait bounds for capability requirements:
pub trait Coordinator: Send + Sync {
    // Guarantees thread-safe coordination
}
3. Async traits for non-blocking operations:
#[async_trait]
pub trait MoFAAgent: Send + Sync {
    async fn execute(&mut self, input: AgentInput) -> AgentResult<AgentOutput>;
}

Performance Optimizations

The microkernel is designed for zero-cost abstractions:
  • No dynamic dispatch where avoidable: Use generics instead of dyn Trait
  • Inline hints: #[inline] on hot path methods
  • Zero-copy where possible: References and Cow<str> for strings
  • Compile-time polymorphism: Generic constraints instead of runtime checks
// Zero-cost generic dispatch
pub fn register_tool<T: Tool + 'static>(tool: T) {
    // Monomorphized at compile time
}

// vs slower dynamic dispatch
pub fn register_tool_dyn(tool: Box<dyn Tool>) {
    // Runtime vtable lookup
}

Next Steps

Dual-Layer Plugins

Learn how plugins extend the microkernel

Agent Coordination

Multi-agent collaboration patterns

Quick Start

Build your first agent

Build docs developers (and LLMs) love