Architecture
Layer stack, workspace structure, crate dependencies, and build pipeline powering the Helix kernel.
Layer Stack
Helix follows a strict layered architecture — every layer only depends on layers below it, never above. This is enforced at compile time through Cargo dependency declarations: if crate A depends on crate B, crate B cannot depend on crate A.
Layer Rules
- No upward dependencies —
helix-halnever importshelix-core. Subsystems access hardware only through HAL traits. - No circular dependencies — Cargo enforces this at workspace resolution time.
- Trait-based abstraction — Each layer boundary is defined by traits:
HalImpl,Subsystem,Module,SyscallHandler. - Compile-time target selection — Architecture-specific code is selected via
#[cfg(target_arch)], not runtime dispatch.
Workspace Structure
The Cargo workspace at the root defines 16 member crates plus the Lumina graphics sub-workspace and the Magma GPU driver sub-workspace:
| Crate | Path | Lines | Description |
|---|---|---|---|
helix-core | core/ | ~2,800 | Kernel orchestrator, IPC, syscalls, self-heal |
helix-hal | hal/ | ~51,300 | Hardware abstraction — x86_64, AArch64, RISC-V |
helix-limine | boot/limine/ | ~1,200 | Limine Protocol Rev 2 boot |
helix-multiboot2 | boot/multiboot2/ | ~400 | Multiboot2 spec boot |
helix-uefi | boot/uefi/ | ~148,000 | Full UEFI platform (130+ files, 57 modules) |
helix-memory | subsystems/memory/ | ~2,200 | Physical/virtual memory management |
helix-execution | subsystems/execution/ | ~2,500 | Threads, scheduler, processes |
helix-dis | subsystems/dis/ | ~11,600 | Dynamic Intent Scheduling engine |
helix-userspace | subsystems/userspace/ | ~3,500 | ELF loader, shell, syscall ABI |
helix-relocation | subsystems/relocation/ | ~2,000 | ELF relocation + KASLR engine |
helix-nexus | subsystems/nexus/ | ~90,000+ | AI/ML intelligence (SYMBIOSIS) |
helix-init | subsystems/init/ | ~4,500 | 5-phase initialization framework |
helix-modules | modules/ | ~3,800 | Module trait, registry, ABI, hot-reload |
helix-fs | fs/ | ~12,000 | HelixFS — CoW B-tree filesystem |
helix-benchmarks | benchmarks/ | ~500 | Benchmark suite |
helix-minimal-os | profiles/minimal/ | ~1,700 | Kernel entry point + demos |
helix-scheduler-round-robin | modules_impl/schedulers/round_robin/ | ~400 | Example module: round-robin scheduler |
Sub-workspaces
| Workspace | Path | Crates | Description |
|---|---|---|---|
| Lumina | graphics/ | 18 crates | GPU graphics pipeline — SPIR-V, render graph, PBR |
| Magma | drivers/gpu/magma/ | 52+ crates | GPU driver — PCI HAL, VRAM, Vulkan 1.3, OpenGL 4.6 |
Workspace Metadata
[workspace.package]
version = "0.4.0" # codename "Aurora"
edition = "2021"
rust-version = "1.75.0"
license = "MIT"
[workspace]
members = [
"core", "hal", "boot/limine", "boot/multiboot2", "boot/uefi",
"subsystems/memory", "subsystems/execution", "subsystems/dis",
"subsystems/userspace", "subsystems/relocation", "subsystems/nexus",
"subsystems/init", "modules", "modules_impl/schedulers/round_robin",
"fs", "benchmarks", "profiles/minimal",
]
Crate Dependency Graph
Below is the full dependency graph showing how every crate connects. Arrows point from dependent to dependency:
Dependency Rules
| Rule | Rationale |
|---|---|
| Subsystems depend on HAL, never on Core | Subsystems are reusable building blocks |
| Core depends on HAL and subsystems | Core orchestrates subsystem lifecycle |
| Modules depend on HAL only | Modules use trait objects, not concrete types |
| Boot crates are standalone | Boot code runs before anything else exists |
helix-fs has zero dependencies | Filesystem logic is pure Rust, fully portable |
helix-relocation uses edition 2024 | Needs latest Rust features for ELF parsing |
helix-nexus is self-contained | AI engine uses only libm + spin |
External Dependencies (workspace-wide)
| Crate | Version | Used By | Purpose |
|---|---|---|---|
spin | 0.9 | nexus, uefi, dis | Spinlock-based mutexes (no_std) |
bitflags | 2.x | dis, memory, hal | Type-safe bitfield flags |
libm | 0.2 | nexus | Math functions for AI (no_std) |
heapless | 0.8 | limine | Fixed-size collections (no_std) |
The entire kernel uses only 4 external crates. Everything else is written from scratch.
Project Metrics
| Metric | Value |
|---|---|
| Total workspace crates | 17 (+ 18 Lumina + 52 Magma) |
| Total estimated lines | 350,000+ |
| Rust edition | 2021 (2024 for relocation) |
| Nightly features | abi_x86_interrupt, allocator_api, naked_functions, asm_const, const_mut_refs, inline_const, negative_impls |
| Target architectures | 3 (x86_64, AArch64, RISC-V 64) |
| Lumina sub-crates | 18 (core, math, shader, IR, SPIR-V, render, pipeline, material, mesh, backend, sync, memory, debug, scene, 3d, ui, tools, inspector) |
| Magma sub-crates | 52+ (core, hal, mem, cmd, rpc, gl, vulkan, and sub-components) |
| NEXUS feature flags | 96+ |
| UEFI modules | 57 |
| Boot protocols | 3 (Limine, Multiboot2, UEFI) |
| Linker scripts | 5 |
| Syscalls implemented | 47 (42 POSIX + 5 Helix-specific) |
| Shell built-in commands | 16 |
| Module types | 8 (Essential, HotReloadable, Userspace, Driver, Filesystem, Scheduler, Allocator, Security) |
Design Principles
Helix is designed around five core principles that guide every architectural decision:
1. Compile-Time Safety
Rust's type system and ownership model are used extensively to prevent entire classes of kernel bugs at compile time:
- No null pointers —
Option<T>everywhere - No data races —
Send/Syncbounds enforced on all shared state - No buffer overflows — bounds-checked arrays, no raw indexing in safe code
- No use-after-free — ownership model prevents dangling references
panic = "abort"— no unwinding in kernel space, predictable failure mode
2. Trait-Driven Abstraction
Every major subsystem boundary is defined by a trait, enabling pluggable implementations:
3. Zero External Dependencies (almost)
The kernel uses only 4 external crates (spin, bitflags, libm, heapless). Everything else — schedulers, allocators, filesystems, graphics, AI — is written from scratch.
4. Hot-Reloadable Modules
The module system supports runtime replacement without downtime:
- Save module state via
get_state()→ModuleState - Unload old module binary
- Load new module binary, verify ABI compatibility
- Restore state via
restore_state(ModuleState) - Resume execution
5. Layered Initialization
The kernel boots through 5 strictly-ordered phases, each unlocking new capabilities:
| Phase | Index | Capabilities Unlocked |
|---|---|---|
| Boot | 0 | Console, serial output |
| Early | 1 | Heap allocator, basic memory |
| Core | 2 | Interrupts, timers, scheduler |
| Late | 3 | Drivers, filesystem, IPC |
| Runtime | 4 | Userspace, modules, networking |
Build Profiles
Helix defines six Cargo build profiles tailored for different use cases:
[profile.dev]
opt-level = 0 # No optimization — fastest builds
lto = false
debug = true # Full debug info
panic = "abort" # No unwinding in kernel
codegen-units = 256 # Maximum parallelism
[profile.release]
opt-level = 3 # Maximum optimization
lto = "fat" # Full link-time optimization
debug = false # No debug info
panic = "abort"
codegen-units = 1 # Best optimization (single CGU)
[profile.release-debug]
inherits = "release"
debug = true # Release speed + debug symbols
[profile.production]
inherits = "release" # Alias for CI/CD pipelines
[profile.bench]
opt-level = 3
lto = "thin" # Faster than fat LTO, still good
panic = "abort"
[profile.test]
opt-level = 1 # Some optimization for realistic tests
debug = true
panic = "abort"
Profile Selection Guide
| Use Case | Profile | LTO | Debug | Opt |
|---|---|---|---|---|
| Day-to-day development | dev | No | Yes | 0 |
| Performance testing | release | Fat | No | 3 |
| Debugging release issues | release-debug | Fat | Yes | 3 |
| CI/CD deployment | production | Fat | No | 3 |
| Benchmark runs | bench | Thin | No | 3 |
| Unit test execution | test | No | Yes | 1 |
All profiles use
panic = "abort"— stack unwinding is not supported in kernel space.
Toolchain
The project pins a specific nightly via rust-toolchain.toml:
[toolchain]
channel = "nightly"
components = ["rust-src", "rustfmt", "clippy", "llvm-tools-preview"]
targets = ["x86_64-unknown-none"]
Required Components
| Component | Purpose |
|---|---|
rust-src | Required for -Zbuild-std — builds core/alloc from source for no_std targets |
rustfmt | Code formatting: max_width = 100, tab_spaces = 4, imports_granularity = "Crate" |
clippy | Lint enforcement: -D warnings (all warnings are errors) |
llvm-tools-preview | llvm-objcopy for binary stripping, llvm-objdump for disassembly |
Compile Targets
| Target Triple | Architecture | Usage |
|---|---|---|
x86_64-unknown-none | x86_64 | Primary kernel target (bare-metal, no OS) |
aarch64-unknown-none | AArch64 | ARM 64-bit kernel target |
riscv64gc-unknown-none-elf | RISC-V 64 | RISC-V kernel target (GC extensions) |
x86_64-unknown-linux-gnu | Linux host | Unit tests run on host OS |
Nightly Features Used
| Feature | Stability | Where Used |
|---|---|---|
abi_x86_interrupt | Unstable | HAL interrupt handlers — x86 calling convention |
allocator_api | Unstable | Custom allocators — Allocator trait |
naked_functions | Unstable | Context switch — no prologue/epilogue |
asm_const | Unstable | Inline assembly constants |
const_mut_refs | Unstable | Compile-time mutable references |
inline_const | Unstable | Inline const expressions in patterns |
negative_impls | Unstable | impl !Send for Type — safety invariants |
Linker Scripts
Helix ships five linker scripts for different boot protocols. All higher-half scripts map the kernel to the top 2 GB of virtual address space.
| Script | Protocol | Base Address | Key Feature |
|---|---|---|---|
profiles/minimal/linker.ld | Multiboot2 | 0x100000 (1 MB) | Flat mapping, .multiboot_header first |
profiles/minimal/linker_pie.ld | Multiboot2 PIE | 0xFFFFFFFF80000000 | KASLR-ready with .dynamic + GOT |
profiles/common/linker_base.ld | Universal | 0xFFFFFFFF80000000 | RELRO, PLT/GOT, Limine requests section |
profiles/limine/linker.ld | Limine PIE | 0xFFFFFFFF80000000 | .limine_requests section, HHDM support |
profiles/uefi/linker.ld | UEFI PIE | 0xFFFFFFFF80000000 | AT() load addresses, .text.hot/.text.cold |
Memory Layout
All higher-half linker scripts produce the following virtual memory layout:
Exported Symbols
Every linker script exports these symbols for use in Rust code:
__kernel_start, __kernel_end # Kernel image boundaries
__text_start, __text_end # Code section
__rodata_start, __rodata_end # Read-only data
__data_start, __data_end # Mutable data
__bss_start, __bss_end # Zero-init section
__got_start, __got_end # GOT (for KASLR relocation)
__rela_start, __rela_end # Relocation entries
KERNEL_PHYS_BASE, KERNEL_VIRT_BASE # Physical/virtual base addresses
These are accessed in Rust via extern "C" blocks and used during early boot to set up paging and perform KASLR relocation.
Boot Sequence
The boot flow from power-on to kernel_main passes through 5 initialization phases managed by helix-init:
Init Subsystems
The init framework registers 13 subsystems with dependency ordering:
| Subsystem | Phase | Priority | Dependencies |
|---|---|---|---|
| Boot | 0 | 0 | None |
| CPU | 1 | 10 | Boot |
| Debug | 1 | 20 | Boot |
| Memory | 1 | 30 | CPU |
| Interrupts | 2 | 40 | Memory |
| Timers | 2 | 50 | Interrupts |
| Scheduler | 2 | 60 | Timers, Memory |
| IPC | 3 | 70 | Scheduler |
| Drivers | 3 | 80 | Interrupts, Memory |
| Filesystem | 3 | 85 | Drivers, Memory |
| Security | 3 | 90 | Memory, IPC |
| Network | 4 | 100 | Drivers, IPC |
| Userland | 4 | 110 | Filesystem, Security, Scheduler |
KernelState Machine
The kernel transitions through a state machine managed by KernelOrchestrator:
Each state transition triggers registered LifecycleCallback handlers.
Build Commands
Make Targets
make build # Release build (opt-level 3, fat LTO)
make build-debug # Debug build (opt-level 0, full debug info)
make qemu # Build + launch in QEMU
make qemu-debug # Build debug + QEMU with GDB server on :1234
make test # Full test suite (integration + unit)
make test-unit # Unit tests only (host target)
make clippy # Lint check (-D warnings)
make fmt # Format all code (rustfmt)
make fmt-check # Verify formatting without changes
make docs # Generate rustdoc (private items)
make iso # Create bootable ISO image
make clean # Remove build/ and target/
make pre-commit # fmt-check + clippy + test (CI gate)
make size # Show kernel binary size breakdown
Just Recipes
just build # Release build
just watch # Rebuild on file changes (cargo-watch)
just doc-serve # Serve docs on localhost:8000
just audit # cargo audit + cargo deny check
Docker
docker compose run dev # Interactive dev shell with all tools
docker compose run build # One-shot release build
docker compose run test # Full test suite in container
docker compose run qemu # QEMU with port forwarding (1234, 5900)
Nix
nix develop # Enter development shell with all deps
nix build # Reproducible release build
Data Flow
Understanding how data flows through the kernel helps grasp the architecture:
Syscall Path
IPC Path
Module Hot-Reload Path
Security Architecture
Security is embedded at every layer of the kernel:
Capability-Based Access Control
The CapabilityBroker in helix-core manages fine-grained permissions:
Isolation Levels (DIS)
The Dynamic Intent Scheduling system defines 5 isolation levels:
| Level | Name | Guarantees |
|---|---|---|
| 0 | None | No isolation — kernel threads |
| 1 | Basic | Separate address space |
| 2 | Standard | Capability-restricted |
| 3 | Strict | Resource-limited, syscall-filtered |
| 4 | Maximum | Hardware-isolated (future: VT-x) |
Protection Keys (Memory)
The memory subsystem supports Intel PKU for per-page protection domains:
ProtectionFlags:NONE,READ,WRITE,EXECUTE,RW,RX,RWXProtectionDomain: Groups pages under a single protection keyGuardPage: Unmapped pages surrounding stacks to catch overflows
Error Handling Strategy
Helix uses a consistent error handling pattern across all crates:
- Each crate defines its own error enum —
KernelError,MemError,HalError,ModuleError, etc. - All errors are
Send + Sync— safe to pass across thread boundaries - No panics in normal paths —
Result<T, E>everywhere - Panic = kernel halt —
panic = "abort"in all profiles, customPanicHandlerlogs context - Self-healing for recoverable failures —
SelfHealingManagermonitors subsystem health and triggers automatic recovery (max 3 restart attempts)
Panic Handling
The PanicContext captures: message, location (file:line:col), backtrace, CPU state, and the current thread ID.