The helix-modules crate is what makes Helix truly modular. It provides a complete module framework with two API versions (v1 legacy and v2 recommended), a registry for discovery, a dependency resolver for startup ordering, a loader for dynamic loading, and an interface system for cross-module communication.
Every pluggable component in Helix — schedulers, allocators, filesystems, drivers — is a module.
shutdown signal ▶ Register Module registered in… 1 init() One-time initializat… 2 start() Module goes live 2 Event/Request Loop Normal operation 2 stop() Graceful shutdown 2 ■ Unregister Removed from kernel 1
☝ Drag to pan · 🤏 Pinch to zoom · Tap a node
Ctrl+F Search
P Path
S Stats
F Fullscreen
E Export
Shift+Drag Move node
↑↓ Navigate
+/− Zoom
This is the preferred API for new modules. It provides lifecycle management, event handling, health monitoring, and state persistence for hot-reload.
pub trait ModuleTrait : Send + Sync {
fn info ( & self ) -> ModuleInfo ; // REQUIRED
fn init ( & mut self , ctx : & Context ) -> Result < ( ) , ModuleError > ; // REQUIRED
fn start ( & mut self ) -> Result < ( ) , ModuleError > ; // REQUIRED
fn stop ( & mut self ) -> Result < ( ) , ModuleError > ; // REQUIRED
// Optional — override for custom behavior
fn handle_event ( & mut self , _event : & Event ) -> EventResponse {
fn handle_request ( & mut self , _request : & Request ) -> Result < Response , ModuleError > {
Ok ( Response :: err ( "Not implemented" ) )
fn is_healthy ( & self ) -> bool { true }
fn save_state ( & self ) -> Option < Vec < u8 >> { None }
fn restore_state ( & mut self , _state : & [ u8 ] ) -> Result < ( ) , ModuleError > { Ok ( ( ) ) }
Index ModuleTrait info init start stop handle_event handle_request is_healthy save_state restore_state
Module metadata is declared with a const builder pattern. This means module info is computed at compile time — no runtime overhead.
pub version : ModuleVersion ,
pub description : & 'static str ,
pub author : & 'static str ,
pub license : & 'static str ,
pub dependencies : & 'static [ & 'static str ] ,
pub provides : & 'static [ & 'static str ] ,
// Const builder — all computed at compile time
pub const fn new ( name : & 'static str ) -> Self ;
pub const fn version ( self , major : u16 , minor : u16 , patch : u16 ) -> Self ;
pub const fn description ( self , desc : & 'static str ) -> Self ;
pub const fn author ( self , author : & 'static str ) -> Self ;
pub const fn flags ( self , flags : ModuleFlags ) -> Self ;
pub const fn dependencies ( self , deps : & 'static [ & 'static str ] ) -> Self ;
pub const fn provides ( self , caps : & 'static [ & 'static str ] ) -> Self ;
Index ModuleInfo ModuleInfo
Flags control module behavior and capabilities. Combine them with bitwise OR.
Flag Bit Description ESSENTIAL1 << 0Cannot be unloaded — kernel panics if it fails HOT_RELOADABLE1 << 1Supports live replacement without restarting USERSPACE1 << 2Runs in user ring (ring 3) DRIVER1 << 3Hardware driver module FILESYSTEM1 << 4Filesystem implementation SCHEDULER1 << 5Scheduler implementation ALLOCATOR1 << 6Memory allocator module SECURITY1 << 7Security policy module EXPERIMENTAL1 << 8Unstable — testing only
Modules receive events from the kernel and can handle requests from other modules. This is the primary communication mechanism.
Tick { timestamp_ns : u64 } ,
MemoryPressure { level : MemoryPressureLevel } ,
CpuHotplug { cpu_id : u32 , online : bool } ,
Custom { name : String , data : Vec < u8 > } ,
pub enum EventResponse { Handled , Ignored , Error ( String ) }
pub source : & 'static str ,
pub request_type : String ,
pub error : Option < String > ,
pub fn ok ( payload : Vec < u8 > ) -> Self ;
pub fn ok_empty ( ) -> Self ;
pub fn err ( message : impl Into < String > ) -> Self ;
Index Event EventResponse Request Response Response ok ok_empty err
Here's a full, working module that implements a custom scheduler. This is the pattern you'll follow for any new module.
modules_impl/my_scheduler/src/lib.rs
use helix_modules :: v2 :: * ;
use helix_modules :: { ModuleError , ModuleFlags } ;
impl ModuleTrait for MyScheduler {
fn info ( & self ) -> ModuleInfo {
ModuleInfo :: new ( "my-scheduler" )
. description ( "Custom round-robin scheduler" )
. flags ( ModuleFlags :: SCHEDULER | ModuleFlags :: HOT_RELOADABLE )
. dependencies ( & [ "helix-execution" ] )
. provides ( & [ "scheduler" , "cpu.scheduling" ] )
fn init ( & mut self , ctx : & Context ) -> Result < ( ) , ModuleError > {
let _time_slice = ctx . config_or ( "time_slice_ms" , "10" ) ;
fn start ( & mut self ) -> Result < ( ) , ModuleError > { Ok ( ( ) ) }
fn stop ( & mut self ) -> Result < ( ) , ModuleError > { Ok ( ( ) ) }
fn handle_event ( & mut self , event : & Event ) -> EventResponse {
_ => EventResponse :: Ignored ,
fn handle_request ( & mut self , req : & Request ) -> Result < Response , ModuleError > {
match req . request_type . as_str ( ) {
"get_stats" => Ok ( Response :: ok (
format! ( "tasks: {}" , self . task_count ) . into_bytes ( )
_ => Ok ( Response :: err ( "Unknown request" ) ) ,
fn is_healthy ( & self ) -> bool { self . initialized }
// Hot-reload: serialize state before unload
fn save_state ( & self ) -> Option < Vec < u8 >> {
Some ( self . task_count . to_le_bytes ( ) . to_vec ( ) )
// Hot-reload: restore state after reload
fn restore_state ( & mut self , state : & [ u8 ] ) -> Result < ( ) , ModuleError > {
self . task_count = u64 :: from_le_bytes (
state [ .. 8 ] . try_into ( ) . unwrap ( )
Index MyScheduler MyScheduler info init start stop handle_event handle_request is_healthy save_state restore_state
The registry is the central catalog of all modules in the kernel. It supports querying by name, ID, flags, or provided capabilities.
pub struct ModuleRegistry { /* ... */ }
pub fn register ( & self , metadata : ModuleMetadata ) -> ModuleResult < ModuleId > ;
pub fn unregister ( & self , id : ModuleId ) -> ModuleResult < ( ) > ;
pub fn get ( & self , id : ModuleId ) -> Option < ModuleMetadata > ;
pub fn get_by_name ( & self , name : & str ) -> Option < ModuleMetadata > ;
pub fn get_providers ( & self , capability : & str ) -> Vec < ModuleId > ;
pub fn list_all ( & self ) -> Vec < ModuleMetadata > ;
pub fn list_by_flag ( & self , flag : ModuleFlags ) -> Vec < ModuleMetadata > ;
pub fn list_running ( & self ) -> Vec < ModuleMetadata > ;
pub fn registry ( ) -> & 'static ModuleRegistry ;
Index ModuleRegistry ModuleRegistry register unregister get get_by_name get_providers list_all list_by_flag list_running registry
The dependency resolver builds a directed acyclic graph (DAG) of module dependencies and computes a topological sort for startup order. It detects circular dependencies at registration time.
Dependency Resolution Pipeline 5N · 4E graph built no cycles sorted order ▶ All Modules Raw module set 1 Build DAG Construct dependency… 2 Cycle Detection Detect circular deps 2 Topological Sort Determine startup or… 2 ■ Ordered Startup Init modules in orde… 1
☝ Drag to pan · 🤏 Pinch to zoom · Tap a node
Ctrl+F Search
P Path
S Stats
F Fullscreen
E Export
Shift+Drag Move node
↑↓ Navigate
+/− Zoom
modules/src/dependency.rs
pub struct DependencyResolver < 'a > {
registry : & 'a ModuleRegistry ,
impl < 'a > DependencyResolver < 'a > {
pub fn resolve ( & self , metadata : & ModuleMetadata ) -> ModuleResult < Vec < ModuleId >> ;
pub fn can_satisfy ( & self , metadata : & ModuleMetadata ) -> bool ;
pub fn unload_order ( & self , id : ModuleId ) -> ModuleResult < Vec < ModuleId >> ;
pub struct DependencyGraph { /* directed acyclic graph */ }
pub fn add_module ( & mut self , id : ModuleId ) ;
pub fn add_dependency ( & mut self , from : ModuleId , to : ModuleId ) ;
pub fn has_cycle ( & self ) -> Option < Vec < ModuleId >> ;
pub fn topological_sort ( & self ) -> ModuleResult < Vec < ModuleId >> ;
Index DependencyResolver DependencyResolver resolve can_satisfy unload_order DependencyGraph DependencyGraph add_module add_dependency has_cycle topological_sort
Modules can expose named interfaces that other modules discover at runtime. This enables loose coupling — a filesystem module doesn't need to know which block device driver is loaded, it just queries for the helix.block_device interface.
modules/src/interfaces.rs
pub trait ModuleInterface : Send + Sync {
fn interface_name ( & self ) -> & 'static str ;
fn interface_version ( & self ) -> ( u16 , u16 ) ;
fn handle_message ( & self , msg : & ModuleMessage ) -> ModuleResult < Option < ModuleMessage >> ;
fn supported_operations ( & self ) -> Vec < & 'static str > ;
// Standard interface names
pub const SCHEDULER : & str = "helix.scheduler" ;
pub const ALLOCATOR : & str = "helix.allocator" ;
pub const FILESYSTEM : & str = "helix.filesystem" ;
pub const BLOCK_DEVICE : & str = "helix.block_device" ;
pub const NETWORK : & str = "helix.network" ;
pub const SECURITY : & str = "helix.security" ;
// Discover modules implementing an interface
let schedulers = interface_registry ( ) . get_implementors ( "helix.scheduler" ) ;
let primary = interface_registry ( ) . get_primary ( "helix.scheduler" ) ;
Index ModuleInterface interface_name interface_version handle_message supported_operations interfaces SCHEDULER ALLOCATOR FILESYSTEM BLOCK_DEVICE NETWORK SECURITY
Helix provides macros to reduce boilerplate when defining modules. The module_v2! macro generates the struct, trait implementation, and registration code from a compact declaration.
// Preferred: v2 declarative macro
description : "Example module" ,
fn init ( & mut self , ctx : & Context ) -> Result < ( ) , ModuleError > { Ok ( ( ) ) }
fn start ( & mut self ) -> Result < ( ) , ModuleError > { Ok ( ( ) ) }
fn stop ( & mut self ) -> Result < ( ) , ModuleError > { Ok ( ( ) ) }
// Register as an interface implementor
impl_interface! ( MyStruct , "helix.my_interface" , ( 1 , 0 ) ) ;
Index MyModule init start stop
The module system is the most important crate to understand if you're building on Helix. Every new feature — scheduler, driver, filesystem, security policy — is a module. The registry, dependency resolver, and interface system ensure that modules can be composed, replaced, and hot-swapped without breaking the system.