Skip to content

Sovereign Systems: Beyond Rust & Zig

“The compiler doesn’t stop at the software boundary. It reaches through to the hardware and pulls the truth back up into the type system.”

Every systems language promises control. C gives you the machine and hopes you survive. Rust gives you a borrow checker and calls it safety. Zig gives you explicit everything and washes its hands at the hardware boundary.

Janus :sovereign does something none of them dare: it makes hardware safety a compile-time property.

Not a library. Not a convention. Not a runtime check that fires too late. A type error — caught before the binary is emitted.


Before we build, let’s bury the myths. Rust and Zig are excellent languages. They are also fundamentally incomplete for the domains where failure means dead satellites, crashed rovers, and corrupted flight controllers.

no_std is a lie of omission. Strip out the standard library and what remains is a language that still assumes a heap allocator exists somewhere. Box, Vec, String – the entire ownership model’s ergonomics collapse without alloc. You’re left writing C-with-lifetimes.

Panic infrastructure is a landmine. In space, panic!() means a dead satellite. Rust’s answer? #[panic_handler] – a single global function you wire up yourself. No structured recovery. No per-subsystem isolation. One panic path for the entire firmware.

MMIO is unsafe soup. Every register access is unsafe. Every DMA buffer is unsafe. Every interrupt handler is unsafe. When 80% of your bare-metal code is unsafe, the borrow checker isn’t protecting you – it’s just making you type more. The safety model evaporates exactly where you need it most.

No deterministic execution guarantees. Rust’s iterators, closures, and trait dispatch can introduce non-obvious allocations and unpredictable codegen. For hard real-time – satellite attitude control loops at 100Hz – you need proof that a function completes in N cycles. Rust gives you nothing.

Zig is honest about the machine but silent about intent. @intToPtr and @ptrToInt are clean, but there’s no semantic difference between “this is a hardware register at 0x4000_1000” and “this is a random pointer cast.” The compiler can’t help you because it can’t see what you meant.

No radiation hardening at the language level. Zig’s comptime is powerful, but it doesn’t know about bit-flips. Single Event Upsets corrupt memory in space. Zig treats memory as reliable. On a satellite, that assumption kills.

Allocator model is beautiful but incomplete. Zig forces explicit allocators – brilliant for desktop, but in bare-metal you often need zero dynamic allocation, ever. Zig doesn’t enforce this; it just makes it possible. “Possible” isn’t “proven.”

No formal link between hardware and safety. Zig’s packed struct maps registers, but there’s no capability model saying “this peripheral is owned by this subsystem and only this subsystem.” You enforce that by convention. Convention doesn’t survive a 3 AM debugging session on a flight model.


Stack overflow is the most common silent killer in systems programming. Every language treats it as a runtime problem. Janus treats it as a compile-time property.

Wall 1: Compile-Time Stack Budget Analysis

Section titled “Wall 1: Compile-Time Stack Budget Analysis”
// :sovereign profile — pledge-based stack budgets
func deep_recursion(n: u32) !u32
pledge stack(4096) // Compiler enforces: this call chain uses <= 4KB
do
if n == 0 do return 1 end
return deep_recursion(n - 1)?
// Compiler: "unbounded recursion — pledge violated"
end

The compiler statically analyzes call graphs and rejects programs that might exceed their pledge. No runtime check needed – the violation is caught before the binary is emitted.

// A fiber's stack is a capability — bounded, revocable, delegable
fiber spawn("worker", capabilities: [
stack(size: 64KB, guard: true), // Guard page is a first-class concept
memory(heap: 1MB),
channel(#sys_log, perms: .write_only),
])

The language knows guard pages exist. The compiler emits the guard_register call. The runtime never forgets to set one up.

@canary // Emits a per-frame random canary check
func parse_untrusted_input(buf: []u8) !Parsed do
// Security-critical code gets defense-in-depth
end
LayerMechanismCostCatches
Compile-timepledge stack(N) analysisZeroStatically provable overflows
Guard pageMMU-enforced hardware trapZeroOverflows that escape static analysis
Software canaryOptional @canary per-function~2 instructionsIntra-page corruption (buffer overwrite within the same page)

No other language offers all three simultaneously. Rust panics on overflow in debug or silently corrupts in release. Zig disables stack probing in freestanding mode. C relies on -fstack-protector, which adds overhead to every function.

Janus :sovereign is the first language where stack safety is a compile-time-verifiable, hardware-enforced, capability-gated property.


Every feature below maps to a real failure mode that has killed actual missions.

// The register map IS the type. Not a wrapper. Not unsafe. The type.
peripheral UART0 at 0x4000_1000
register CR: u32
field TXEN: bit[0] = .disabled // Reset value is part of the type
field RXEN: bit[1] = .disabled
field BAUD: bits[2..5]
end
register DR: u32
field DATA: bits[0..7]
field FERR: bit[8] read_only // Compiler rejects writes to read-only fields
end
end

The compiler knows FERR is read-only. It knows CR is at offset 0x00 from base 0x4000_1000. No unsafe. No pointer casts. Syntactic Honesty: the peripheral declaration is the hardware truth.

Rust makes you write unsafe { core::ptr::write_volatile(0x4000_1000 as *mut u32, val) }. That’s not programming – that’s praying with extra steps.

// TMR (Triple Modular Redundancy) as a type qualifier
let critical_state: tmr u32 = 0x0000_0000
// Compiler emits: three copies + majority vote on every read
// The programmer sees ONE variable. The machine sees three.
critical_state = compute_attitude() // Writes to all three
let val = critical_state // Majority vote, auto-corrects single bit-flip

The tmr qualifier reveals that this memory is triple-redundant. You don’t hide it behind a library. You don’t pretend it’s normal memory. The type system knows the hardware is unreliable and the codegen compensates.

No other language has this. Not Rust. Not Zig. Not Ada/SPARK – which gets closest but buries it in annotations.

3. WCET Pledges (Worst-Case Execution Time)

Section titled “3. WCET Pledges (Worst-Case Execution Time)”
func attitude_control_loop(sensors: *SensorArray) !CommandVector
pledge wcet(10_000 cycles) // Hard ceiling
pledge no_alloc // Zero dynamic allocation — compiler enforced
pledge no_recursion // Call graph must be a DAG
pledge deterministic // No branches dependent on uninitialized memory
do
// If any code path MIGHT exceed 10,000 cycles,
// the compiler rejects the program. At compile time. Not at runtime.
end

The compiler does static analysis on the call graph, counts instructions per path, and refuses to emit a binary if the pledge can’t be proven.

Rust has zero concept of this. Zig has zero concept of this. Even Ada/SPARK requires external tools (RapiTime, aiT) to analyze WCET – it’s not in the language.

Janus makes WCET a compile-time property. The pledge is part of the function’s type signature. If you call a function that violates your WCET budget, you get a type error. Not a runtime overrun. Not a missed deadline. A type error.

// Subsystem A owns UART0. Period. Compiler-enforced.
capsule navigation
requires peripheral(UART0, perms: .read_write)
requires peripheral(SPI1, perms: .read_only)
pledge no_alloc
do
func send_telemetry(data: []const u8) !void do
UART0.DR.DATA = data[0] // Legal: we own UART0
// SPI1.DR.DATA = 0xFF // COMPILE ERROR: read_only permission
end
end
capsule payload_science
requires peripheral(SPI1, perms: .read_write)
do
func read_sensor() !SensorReading do
// UART0.CR.TXEN = .enabled // COMPILE ERROR: no capability for UART0
let raw = SPI1.DR.DATA // Legal: we own SPI1
return raw
end
end

Two capsules. Two peripheral sets. Zero overlap enforced at compile time. No mutex. No runtime lock. The type system guarantees that navigation and payload_science never touch each other’s hardware.

In Rust, you’d use a HAL crate with ownership types – but it’s a library convention, not a language guarantee. Swap HAL crates and your safety model vanishes. In Janus, the compiler is the enforcer.

5. Interrupt Priority Inversion Prevention

Section titled “5. Interrupt Priority Inversion Prevention”
interrupt TIMER0_IRQ priority(3)
pledge wcet(500 cycles)
pledge no_blocking // Cannot acquire any lock held by lower priority
do
// This ISR runs at priority 3.
// If it tries to call a function that acquires a lock
// also used by a priority-2 context — COMPILE ERROR.
heartbeat_counter += 1 // Atomic by declaration, not by prayer
end

Priority inversion killed the Mars Pathfinder in 1997. It took a remote patch via deep space communication to fix a mutex bug. In Janus, the compiler catches it before the binary ships. The pledge no_blocking combined with priority-aware lock analysis means priority inversion is a compile-time error.

6. Watchdog Integration as a Language Primitive

Section titled “6. Watchdog Integration as a Language Primitive”
watchdog system_wdt timeout(2000ms)
func main_loop() never
pledge watchdog(system_wdt) // Compiler verifies: every path kicks within 2000ms
do
loop do
let sensors = read_sensors() // 50ms max
let commands = compute(sensors) // 200ms max (from wcet pledge)
actuate(commands) // 100ms max
kick system_wdt // Explicit. Visible. Honest.
// Delete this line — COMPILE ERROR
// WCET sum exceeds 2000ms — COMPILE ERROR
end
end

Every embedded developer has shipped code that forgot to kick the watchdog on one error path. The satellite resets. Telemetry is lost. In Janus, the compiler proves that every execution path through a pledge watchdog function kicks the dog within the timeout window.

func configure_dma() !void
pledge memory_fence // All MMIO writes ordered; no reordering across fence points
do
DMA1.CR.EN = .disabled // Step 1: disable
fence // Hardware memory barrier emitted here
DMA1.CMAR = buffer.as_ptr() // Step 2: set source
DMA1.CNDTR = buffer.len() // Step 3: set length
fence // Another barrier
DMA1.CR.EN = .enabled // Step 4: enable
// Compiler GUARANTEES step ordering. No volatile tricks. No UB prayers.
end

In C, you’d use volatile and hope the compiler doesn’t reorder. In Rust, you’d use core::sync::atomic::fence(Ordering::SeqCst) – correct but communicates nothing about why the fence exists. In Janus, the fence keyword inside a pledge memory_fence function is self-documenting intent with compiler-verified ordering.


Every feature lands precisely where it belongs in the profile system:

Feature:core:service:sovereign
peripheral declarationsFirst-class
tmr type qualifierFirst-class
pledge wcetFirst-class
pledge no_allocAvailableAvailableEnforced by default
interrupt with priority analysisFirst-class
watchdog primitivesFirst-class
fence with ordering proofFirst-class
Guard page integrationAutomatic (nurseries)Capability-gated
@canary annotationOpt-in
pledge stack(N)First-class

The lower profiles don’t need this – they target hosted environments. :sovereign targets the metal itself, where the compiler has full knowledge of the target hardware.

This maps cleanly onto what already exists:

  • :core — Stack is just memory. No special treatment. (Complete.)
  • :service — Nursery-spawned tasks get automatic guard pages. (Natural fit with the nursery model.)
  • :sovereignpledge stack(N) compile-time analysis + guard page integration + optional @canary annotation. The compiler knows about the hardware floor beneath it.

The guard page infrastructure built in Nexus OS becomes the runtime backing for Janus :sovereign’s stack safety model. The language doesn’t reinvent it – it declares it as a capability and lets the kernel enforce it.


Here’s what Rust and Zig fundamentally can’t fix: they separate the language from the hardware.

Rust’s safety model ends at unsafe. Zig’s explicitness ends at “you figure out the hardware.” Both languages say: “Past this point, you’re on your own.”

Janus :sovereign says: there is no point where you’re on your own. The peripheral map is a type. The execution time is a pledge. The interrupt priority is a constraint. The watchdog is a proof obligation. The radiation protection is a qualifier.

The language and the machine become one legible surface.

That’s what makes it the best systems language on this planet – and the only one worth sending off it.


Related Reading: