Module System

ModuleTrait v2, registry, dependency resolution, interface system, event-driven lifecycle, and macros.

Profile Reference

Module System

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.

Module Lifecycle6N · 5E
shutdown signalRegisterModule registered in…1init()One-time initializat…2start()Module goes live2Event/Request LoopNormal operation2stop()Graceful shutdown2UnregisterRemoved from kernel1
100%
☝ Drag to pan·🤏 Pinch to zoom·Tap a node

This is the preferred API for new modules. It provides lifecycle management, event handling, health monitoring, and state persistence for hot-reload.

modules/src/v2/mod.rs
rust
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
6
7
// Optional — override for custom behavior
fn handle_event(&mut self, _event: &Event) -> EventResponse {
9
EventResponse::Ignored
10
}
fn handle_request(&mut self, _request: &Request) -> Result<Response, ModuleError> {
12
Ok(Response::err("Not implemented"))
13
}
fn is_healthy(&self) -> bool { true }
fn save_state(&self) -> Option<Vec<u8>> { None }
fn restore_state(&mut self, _state: &[u8]) -> Result<(), ModuleError> { Ok(()) }
17
}
Index

Module Info (Const Builder)

Module metadata is declared with a const builder pattern. This means module info is computed at compile time — no runtime overhead.

modules/src/v2/mod.rs
rust
2 refs
pub struct ModuleInfo {
2
pub name: &'static str,
3
pub version: ModuleVersion,
4
pub description: &'static str,
5
pub author: &'static str,
6
pub license: &'static str,
7
pub flags: ModuleFlags,
8
pub dependencies: &'static [&'static str],
9
pub provides: &'static [&'static str],
10
}
11
12
// Const builder — all computed at compile time
2 refs
impl ModuleInfo {
14
pub const fn new(name: &'static str) -> Self;
15
pub const fn version(self, major: u16, minor: u16, patch: u16) -> Self;
16
pub const fn description(self, desc: &'static str) -> Self;
17
pub const fn author(self, author: &'static str) -> Self;
18
pub const fn flags(self, flags: ModuleFlags) -> Self;
19
pub const fn dependencies(self, deps: &'static [&'static str]) -> Self;
20
pub const fn provides(self, caps: &'static [&'static str]) -> Self;
21
}
Index

Module Flags

Flags control module behavior and capabilities. Combine them with bitwise OR.

FlagBitDescription
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

Events & Requests

Modules receive events from the kernel and can handle requests from other modules. This is the primary communication mechanism.

modules/src/v2/mod.rs
rust
pub enum Event {
2
Tick { timestamp_ns: u64 },
3
Shutdown,
4
MemoryPressure { level: MemoryPressureLevel },
5
CpuHotplug { cpu_id: u32, online: bool },
6
Custom { name: String, data: Vec<u8> },
7
}
8
pub enum EventResponse { Handled, Ignored, Error(String) }
10
pub struct Request {
12
pub source: &'static str,
13
pub request_type: String,
14
pub payload: Vec<u8>,
15
}
16
2 refs
pub struct Response {
18
pub success: bool,
19
pub payload: Vec<u8>,
20
pub error: Option<String>,
21
}
22
2 refs
impl Response {
pub fn ok(payload: Vec<u8>) -> Self;
pub fn ok_empty() -> Self;
pub fn err(message: impl Into<String>) -> Self;
27
}
Index

Complete Module Example

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
rust
1
#![no_std]
2
extern crate alloc;
3
4
use helix_modules::v2::*;
5
use helix_modules::{ModuleError, ModuleFlags};
6
2 refs
pub struct MyScheduler {
8
initialized: bool,
9
task_count: u64,
10
}
11
2 refs
impl ModuleTrait for MyScheduler {
fn info(&self) -> ModuleInfo {
14
ModuleInfo::new("my-scheduler")
15
.version(1, 0, 0)
16
.description("Custom round-robin scheduler")
17
.author("Developer")
18
.flags(ModuleFlags::SCHEDULER | ModuleFlags::HOT_RELOADABLE)
19
.dependencies(&["helix-execution"])
20
.provides(&["scheduler", "cpu.scheduling"])
21
}
22
fn init(&mut self, ctx: &Context) -> Result<(), ModuleError> {
24
let _time_slice = ctx.config_or("time_slice_ms", "10");
25
self.initialized = true;
26
Ok(())
27
}
28
fn start(&mut self) -> Result<(), ModuleError> { Ok(()) }
fn stop(&mut self) -> Result<(), ModuleError> { Ok(()) }
31
fn handle_event(&mut self, event: &Event) -> EventResponse {
33
match event {
34
Event::Tick { .. } => {
35
self.task_count += 1;
36
EventResponse::Handled
37
}
38
_ => EventResponse::Ignored,
39
}
40
}
41
fn handle_request(&mut self, req: &Request) -> Result<Response, ModuleError> {
43
match req.request_type.as_str() {
44
"get_stats" => Ok(Response::ok(
45
format!("tasks: {}", self.task_count).into_bytes()
46
)),
47
_ => Ok(Response::err("Unknown request")),
48
}
49
}
50
fn is_healthy(&self) -> bool { self.initialized }
52
53
// Hot-reload: serialize state before unload
fn save_state(&self) -> Option<Vec<u8>> {
55
Some(self.task_count.to_le_bytes().to_vec())
56
}
57
58
// Hot-reload: restore state after reload
fn restore_state(&mut self, state: &[u8]) -> Result<(), ModuleError> {
60
if state.len() >= 8 {
61
self.task_count = u64::from_le_bytes(
62
state[..8].try_into().unwrap()
63
);
64
}
65
Ok(())
66
}
67
}
Index

Module Registry

The registry is the central catalog of all modules in the kernel. It supports querying by name, ID, flags, or provided capabilities.

modules/src/registry.rs
rust
3 refs
pub struct ModuleRegistry { /* ... */ }
2
3 refs
impl 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>;
12
}
13
14
// Global singleton
pub fn registry() -> &'static ModuleRegistry;
Index

Dependency Resolution

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 Pipeline5N · 4E
graph builtno cyclessorted orderAll ModulesRaw module set1Build DAGConstruct dependency…2Cycle DetectionDetect circular deps2Topological SortDetermine startup or…2Ordered StartupInit modules in orde…1
100%
☝ Drag to pan·🤏 Pinch to zoom·Tap a node
modules/src/dependency.rs
rust
2 refs
pub struct DependencyResolver<'a> {
2
registry: &'a ModuleRegistry,
3
}
4
2 refs
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>>;
9
}
10
2 refs
pub struct DependencyGraph { /* directed acyclic graph */ }
12
2 refs
impl DependencyGraph {
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>>;
18
}
Index

Interface System

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
rust
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>;
6
}
7
8
// Standard interface names
pub mod interfaces {
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";
16
}
17
18
// Discover modules implementing an interface
19
let schedulers = interface_registry().get_implementors("helix.scheduler");
20
let primary = interface_registry().get_primary("helix.scheduler");
Index

Macros

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.

modules/src/macros.rs
rust
1
// Preferred: v2 declarative macro
2
module_v2! {
pub struct MyModule {
4
counter: u64 = 0,
5
active: bool = false,
6
}
7
info: {
8
name: "my-module",
9
version: (1, 0, 0),
10
description: "Example module",
11
author: "Developer",
12
}
13
impl {
fn init(&mut self, ctx: &Context) -> Result<(), ModuleError> { Ok(()) }
fn start(&mut self) -> Result<(), ModuleError> { Ok(()) }
fn stop(&mut self) -> Result<(), ModuleError> { Ok(()) }
17
}
18
}
19
20
// Register as an interface implementor
21
impl_interface!(MyStruct, "helix.my_interface", (1, 0));
Index

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.