Kernel Decisions
Nine decisions that define how Rumpk works — and why it doesn't work like Linux.
K1: Unikernel Over Monolithic Kernel
Status: Accepted
Context
Traditional monolithic kernels mix isolation boundaries with scheduling logic: 2000+ cycle context switches, thousands of syscalls, ambient authority security. Microkernels (seL4, MINIX) trade performance for formal verification. Neither fits a system designed for 8-bit MCUs and Mars rovers in the same binary.
Decision
Rumpk is a modular unikernel: single address space, no process boundary, application carries its own OS logic via libnexus.a (the Membrane layer).
Alternatives Rejected
| Option | Why Not |
|---|---|
| Linux monolith | ~2000 cycle hypercall overhead, /proc complexity, ambient authority model |
| Full microkernel (seL4) | 10-100ms latencies, formal verification overhead not justified for commercial systems |
| Container stack (Docker) | Requires underlying OS, 200MB+ runtime overhead per container |
Consequences
- Zero kernel context switches for I/O (2-5 cycle latency)
- Deterministic, verifiable execution paths
- POSIX compatibility requires Membrane translation layer
- Each application instance includes its own kernel code (~280KB)
K2: Zig HAL + Nim Kernel Logic
Status: Accepted
Context
Hardware interactions demand precise memory layouts, volatile access, and atomic operations. Kernel scheduling and fiber management benefit from higher-level abstractions like pattern matching and ARC memory management. One language forces compromise in either direction.
Decision
Split the kernel into two language domains:
hal/(Zig): Direct hardware — UART, VirtIO, GIC, MMU, interrupt vectorscore/(Nim): Scheduler, fibers, channels, VFS, capability enforcement
They meet at the HAL struct — a C-compatible function pointer table.
Alternatives Rejected
| Option | Why Not |
|---|---|
| All Zig | Requires custom async/await runtime; loses Nim's fiber elegance |
| All Nim | Low-level operations become brittle; inline asm escapes are unsafe |
| C for HAL | Equally verbose as Zig but more fragile (no comptime, no safety) |
Consequences
- Clean separation: Physics (Zig) vs. Logic (Nim)
- Nim fibers survive HAL panics (channels persist at fixed addresses)
- Future Janus language can slot into either layer
- Two runtime ecosystems to maintain; ABI discipline required
K3: 12 Frozen Syscalls
Status: Superseded by K7 — Kernel Protocol Boundary (2026-07-02). Retained as historical record.
Superseded
The "12 frozen syscalls" framing has been replaced by the Kernel Protocol doctrine: the kernel boundary is a protocol of typed ION Ring packets, signed BKDL descriptors, and capability verbs, not a fixed syscall table. The trap numbers below remain valid as the current implementation surface of the Trap ABI, but applications should target libnexus rather than the syscall numbers directly. See K7 for the active decision.
Context
POSIX defines 400+ syscalls. Each is a security boundary crossing and a context switch point. Most are vestigial. Nexus routes all bulk I/O through ION Ring buffers — the kernel only needs a handful of control operations.
Decision
Exactly 12 syscalls + 1 meta-slot. Frozen forever:
pledge, unveil, read(0), write(1), map, unmap, spawn, kill, yield, time, random, halt
All data I/O goes through ION Rings (zero context switch). The syscall ABI is compiled into binaries at link time — no versioning, no compat layers.
Alternatives Rejected
| Option | Why Not |
|---|---|
| Full POSIX | 400+ audit points, each with versioning and compat requirements |
| seL4 model | 100+ capability operations, more complex interface |
| Plan 9 RPC | Everything-as-RPC is flexible but adds latency |
Consequences
- Formal verification feasible (12 entry points vs. 400+)
- Security surface trivially auditable
- Binary ABI stable forever (no libc version wars)
- Legacy applications require Membrane translation
- No backfill possible — missed use cases stay missed
K4: ION Rings Over Pipes and Sockets
Status: Accepted
Context
POSIX pipes are byte-streams requiring syscalls per read/write. Sockets add TCP/IP overhead for local communication. Financial exchanges proved the Disruptor pattern achieves nanosecond latencies with zero allocations.
Decision
All IPC uses Disruptor-style ring buffers (ION Rings):
- 128-byte fixed header + dynamic payload in shared memory
- Pre-allocated at boot — no dynamic allocation
- Each fiber gets dedicated RX/TX rings
- Backpressure via atomic head/tail pointers
Alternatives Rejected
| Option | Why Not |
|---|---|
| POSIX pipes | Byte-oriented, syscall per operation, 2-5 µs minimum latency |
| POSIX sockets | TCP overhead, kernel routing, ARP lookups for local IPC |
| Actor model (Erlang) | Elegant but requires heavyweight runtime (50MB Erlang VM) |
Consequences
- ~100ns latency for local IPC (vs. 2-5 µs for pipes)
- Zero-copy with proper buffer ownership
- Automatic backpressure (no overflow, no unbounded queues)
- Fixed-size rings require capacity pre-planning
- Buffer ownership semantics require discipline
K5: Tickless Event-Driven Scheduler
Status: Accepted
Context
Traditional schedulers wake the CPU every 1-10ms via timer interrupt. This prevents deep sleep states and wastes power even when the system is completely idle. Modern hardware (V-Sync, DMA completion, network IRQs) provides natural event signals.
Decision
No periodic scheduler tick. The scheduler is an ISR, not a loop:
- When
RunQueueis empty: CPU executesWFI, power drops to leakage levels - Scheduling occurs only as side-effect of hardware interrupts
- Wakes on V-Sync (120Hz), packet arrival, DMA completion
- Software timeouts registered as explicit deadline events
Alternatives Rejected
| Option | Why Not |
|---|---|
| Fixed tick (1ms) | Forces idle wake-ups, 1-3% CPU overhead on idle |
| Dynamic tick (Linux NO_HZ) | Heuristic tuning adds complexity without dramatic benefit |
| Per-app event loop | Requires application awareness, not transparent |
Consequences
- 5-10% power reduction on idle (mobile/embedded)
- Events trigger immediate scheduler response (deterministic latency)
- No fairness pathology (Spectrum model prevents starvation)
- Software timeouts must be explicitly registered
- Some batch workloads lose natural fairness preemption points
K6: Fibers Over Processes and Threads
Status: Accepted
Context
Processes cost 1-5 MB RAM and 1-5 µs per context switch. Threads share memory but invite race conditions and deadlocks. Nexus operates in a single address space — process isolation provides no benefit, only cost.
Decision
All concurrency uses fibers (Nim async/await):
- ~1 KB RAM per fiber (vs. 1-5 MB per process)
- Cooperative switching: ~100-200 ns (vs. 1-5 µs for context switch)
- No preemption within a priority level — no race conditions by construction
- Spectrum-based fairness: Photon > Matter > Gravity > Void
Alternatives Rejected
| Option | Why Not |
|---|---|
| Heavy processes | Isolation already provided by capabilities; still pay full context switch |
| Shared-memory threads | Lock-free data structures are hard; deadlocks and races omnipresent |
| Actor model (Erlang) | Elegant but 50MB base runtime; too heavy for 280KB kernel |
Consequences
- Thousands of fibers practical (MCUs can run 20+, desktops run 10,000+)
- No race conditions — shared memory with cooperative scheduling
- 4KB default stack per fiber
- No true parallelism on single core (concurrency only)
- Long-running fibers must yield voluntarily
A1: Single Address Space
Status: Accepted
Context
Multiple address spaces (one per process) provide isolation but require TLB flushes on context switch (100-500 cycles). In a unikernel where isolation comes from capabilities, not virtual address separation, the overhead is pure waste.
Decision
One virtual address range for the entire system:
- Cell A:
0x08000000–0x0FFFFFFF - Cell B:
0x10000000–0x17FFFFFF - Isolation is physical (PMP registers / MMU page tables), not VA ranges
- No TLB flush needed when switching between cells
Alternatives Rejected
| Option | Why Not |
|---|---|
| Per-process address spaces | TLB flush = 100-500 cycles per switch |
| ASID tagging | Mitigates flushes but complex hardware requirement, not universal |
| No isolation | Fast but insecure |
Consequences
- Zero TLB flush overhead on cell switches
- Simpler kernel (no VA→PA remapping per cell)
- Shared read-only pages between cells (deduplication)
- Address space must be statically partitioned at boot
- Some hardware (ARM Realm Management) conflicts with this model
A2: SysTable Frozen ABI
Status: Amended by K7 — Kernel Protocol Boundary (2026-07-02). The fixed-address idea survives; the 240-byte size and "12 syscall pointers" claims do not.
Amended
The SysTable remains a fixed-address structure (RISC-V 0x83000000, ARM64 0x50000000) with a magic-number-validated layout. Per SPEC-022 it is now 136 bytes and carries ION ring descriptors + a small set of fast-path function pointers + framebuffer info (not "12 syscall pointers + 1 meta-slot"). The larger doctrinal shift is that the SysTable is now framed as the Trap-ABI entry surface, not the application contract — see K7.
Context
Dynamic registration (UEFI-style) is flexible but requires boot negotiation. Fixed addresses are simple and can be compiled into binaries at link time. The kernel only needs a handful of function pointers.
Decision
SysTable at a fixed physical address, immutable layout:
| Architecture | Address |
|---|---|
| RISC-V 64 | 0x83000000 |
| ARM64 | 0x50000000 |
136 bytes. ION ring descriptors (RX, TX, event, command, input, semantic transport) + a small set of fast-path function pointers (fn_vfs_open, fn_vfs_read, fn_vfs_write, fn_vfs_close, fn_pledge, fn_yield) + framebuffer info. The fixed-address layout never changes across versions; the function-pointer set is allowed to migrate behind ring messages over time.
Alternatives Rejected
| Option | Why Not |
|---|---|
| Dynamic registration | Boot complexity, discovery protocol needed |
| Multiple SysTables per cell | Memory overhead, harder to debug |
| Syscalls only (no table) | Loses zero-copy ring optimization |
Consequences
- Apps hardcode SysTable address — no discovery code
- Applications that target
libnexusare insulated from SysTable evolution - Runtime structure validation trivial (magic + size check)
- Wrong address = hard crash (no fallback)
A3: DragonflyBSD LWKT Scheduler Model
Status: Accepted
Context
Linux CFS optimizes for fairness (O(log n), preemptive). DragonflyBSD LWKT optimizes for predictability (priority-based, cooperative). Nexus needs deterministic latency, not max fairness.
Decision
Adopt DragonflyBSD's Lightweight Kernel Thread model:
- Fixed priority per fiber (Spectrum tier: Photon > Matter > Gravity > Void)
- Round-robin within same priority level
- Cooperative: fibers yield voluntarily, no preemption overhead
- Data moves between cores via ION Ring messages (no shared-memory mutexes)
Alternatives Rejected
| Option | Why Not |
|---|---|
| Linux CFS | Designed for multitenant fairness; preemption adds jitter |
| Rate Monotonic | Requires static scheduling; less flexible for dynamic workloads |
| Custom from scratch | High development risk, debugging nightmare |
Consequences
- Simpler scheduler (fewer lines = fewer bugs)
- Predictable: high-priority fibers always run first
- No starvation: Gravity/Void get time after Photon/Matter sleep
- Not perfectly fair (background jobs may starve under sustained foreground load)
- Long-running fibers must yield() voluntarily
A4: No Microkernel Message-Passing
Status: Accepted
Context
seL4 routes all inter-process communication through the kernel via formal RPC. This enables formal verification but adds ~1 µs overhead per message. Nexus already separates capability checking from data transfer.
Decision
The kernel does not implement RPC or message-passing:
- Kernel only performs capability checks + side effects (memory allocation, pledge verification)
- Data exchange happens via shared ION Rings — userland responsibility
- Kernel is completely passive (no mediator, no routing, no serialization)
Alternatives Rejected
| Option | Why Not |
|---|---|
| seL4-style RPC | Every message = hypercall, ~1 µs overhead each |
| Shared memory without kernel help | Easy to deadlock, hard to debug |
| Hybrid (RPC for control, rings for data) | Complexity without clear benefit over pure rings |
Consequences
- Kernel stays tiny (no RPC logic, no message routing)
- Verification scope smaller (only capability checks, not datapath)
- IPC latency is nanoseconds, not microseconds
- Applications must implement their own wire protocols
- Debugging requires understanding both capability system and app-level protocols
K7: Kernel Protocol Boundary
Status: Accepted
Date: 2026-07-02
Supersedes: K3 — 12 Frozen Syscalls
Amends: A2 — SysTable Frozen ABI
Context
K3 framed the kernel boundary as a fixed table of 12 syscalls "frozen forever." That framing conflated three different things: the CPU trap entry, the kernel's semantic surface, and the per-language calling convention. As the project matured, this conflation produced concrete costs:
- The public syscall reference advertised POSIX-flavored entry numbers (
SYS_OPEN,SYS_READ, integerfd) that contradicted the Native Interface described inSPEC-070. - The SysTable size drifted between 136 bytes (per
SPEC-022) and 240 bytes (per older docs) with no single source of truth. - External readers could not tell whether Nexus was "POSIX with capabilities" or "capability-native with a POSIX shim."
- The "frozen forever" wording blocked additive evolution: new packet types and capability verbs had no clean migration story if they had to slot into a fixed-width table.
The actual implementation had already moved on. The kernel communicates primarily through typed ION Ring packets (SPEC-022), loads code only through signed BKDL descriptors (SPEC-071), and gates every effectful operation through a 7-verb capability algebra (SPEC-051). POSIX-style syscalls survive only as a transitional shim.
Decision
The kernel boundary is a protocol, not a syscall table. The stable contract between userland and kernel is the union of:
- Typed ION Ring packets — the wire format for every channel interaction
- Signed BKDL descriptors — the spawn/load contract for every artifact
- Capability verb algebra — the authority surface (
SPAWN,SEND,RECV,MAP,MASK,TICK,GRANT)
The CPU-facing trap ABI (ecall / svc #0 / syscall) is reframed as an implementation detail of how the protocol is mechanically submitted. It is allowed to change per architecture and per kernel version. Applications target libnexus (the canonical C ABI) or an idiomatic language binding; neither reaches for raw trap numbers.
The ABI is an implementation detail. The protocol is the contract.
Languages are transport adapters. The protocol is the only privileged surface at the kernel boundary. The SDK tiering (SPEC-075-INTERFACE-LANGUAGE-BINDINGS) classifies languages by what they may do at that boundary: Tier 2 Native (Janus, Nim), Tier 3 Bridge (Zig HAL-only, Rust FFI-only, C/C++ Membrane-only).
Alternatives Rejected
| Option | Why Not |
|---|---|
| Keep K3 "12 frozen syscalls" | Conflates trap ABI with semantic surface; blocks additive evolution; contradicts SPEC-070 |
| Reframe as "microkernel message-passing" (seL4 model) | Adds ~1 µs overhead per message; rejected in A4 |
| Reify POSIX as the native surface | Reverses the direction of SPEC-073 (Chimera bridge); POSIX is a compatibility layer, not the contract |
Consequences
- The trap ABI may evolve without breaking applications that target
libnexusor a Tier-2 binding - New packet types and capability verbs are added by spec amendment; wire layouts are additive (read-only after ratification)
- Authority is orthogonal to the protocol: capability bundles are validated atomically at spawn, reducing to the seven verbs
- Compatibility bridges (Chimera POSIX, NipCell Linux, Mimic foreign-driver) translate upward to the protocol; they are not the contract
- The SysTable (A2) remains a fixed-address entry surface, but its contents continue migrating from function pointers to ring descriptors
- Public docs must distinguish Trap ABI / Kernel Protocol / Language Bindings; the word "syscall" in user-facing copy must be qualified by which layer it refers to
References
NORTH-STAR-LIBERTARIA-COMMONWEALTH.md— Protocol boundary doctrine (foundation doctrine file)SPEC-070-INTERFACE-NATIVE-ABI— Native application lifecycle and channel shapeSPEC-071-INTERFACE-BINARY-MANIFEST— BKDL descriptor formatSPEC-022-KERNEL-ION-SYSTABLE— ION Ring layout, SysTable structure (136 bytes)SPEC-075-INTERFACE-LANGUAGE-BINDINGS— Language tier stackSPEC-073-INTERFACE-CHIMERA-POSIX-BRIDGE— POSIX translation layer
K8: Cells Own Application State
Status: Accepted
Context
Every general-purpose OS eventually grows a state daemon. Linux has systemd, D-Bus, udev, gsettings, dconf, and NetworkManager — each one a kingdom that began as a helper and accreted governing power. macOS has launchd and the defaults system. Windows has the Registry. The pattern is consistent: a kernel-side or pid-1-side service that holds application state, mediates access, and slowly becomes the actual application runtime.
The result is always the same. Applications become clients of the state daemon. The daemon gains query semantics, then scripting, then scheduling authority, then policy enforcement. The kernel starts reacting to daemon state instead of governing primitives. Two applications cannot share a knob without the daemon observing, logging, and judging the transaction. The kernel ends up doing things the applications asked it not to.
Nexus hit this threshold at M6 with the Nexus Registry Protocol (NRP). NRP was correctly designed as a protocol, not a database — but its Tier 0 Ambient authentication and Config Ledger backend made every mutation flow through a kernel-side event stream. The architectural pressure is already visible: if two userland apps want to negotiate a slider, the kernel sees the writes, logs them to the System Truth Ledger, and propagates them as system truth. That is wrong. Application state is not system truth. Application state is application truth, owned by the application, observed only by those the application has authorized.
The question is not whether to fix NRP. NRP is correct for system truth. The question is what role the kernel plays when the truth in question is not system truth at all.
Decision
The kernel governs. Cells remember.
Nexus separates two categories of truth:
System truth → TruthDB governs (kernel-side, ledger-backed)
Application truth → Cells own (userland, in-cell, cell-scoped)The kernel's role narrows to four primitives:
Govern → capabilities, pledges, spectra, contracts
Route → ION ring bytes by handle, not by content
Verify → manifest hashes, signatures, contract validity
Survive → hardware faults, cell crashes, reboot boundariesThe kernel does not do: store application state, observe application state, log application mutations, query application state, or schedule based on application state. Those concerns move into the cell.
Six invariants follow:
| # | Invariant |
|---|---|
| 1 | TruthDB governs system truth. Cells own application truth. The two are not mixed at the storage layer. |
| 2 | Application state lives in the cell's heap, persisted to a cell-private NexFS partition, replicated only via an explicit Pact subscriber. |
| 3 | ION transports observations (events), never database queries. The kernel routes bytes; it cannot inspect payload. |
| 4 | The scheduler reacts to events, not stored data. Data carries no scheduling semantics. Priority is event metadata, bounded by the mutator's Spectrum tier. |
| 5 | Objects are passive. Cells execute. An object never calls another object. A cell subscribes, observes, and acts. |
| 6 | No state daemon. No pid-1 registry. No D-Bus successor. Each cell carries its own state library linked as libnexus_clos.a. |
Alternatives Rejected
| Option | Why Not |
|---|---|
| Extend NRP with a cell-local scope flag | Conflates system and application truth at the protocol layer. The Config Ledger write path remains; the kernel still sees every mutation. Does not satisfy invariant 2. |
Run a userland closd service that all cells connect to | Recreates D-Bus with new paint. A central state service inevitably accretes query semantics, scripting hooks, and policy enforcement. Violates invariant 6. |
| Per-cell database files, no protocol | Cells cannot share state. Forces every cross-cell interaction back through TruthDB, which is exactly the problem K8 exists to solve. |
| Put application state in NexFS as typed files (Plan-9 style) | Loses transactional semantics, subscription model, and ownership tracking. Reintroduces heuristic file-scraping. Violates the spirit of ST2. |
| Treat all state as CRDTs from day one | Over-constrains the type system. CRDTs are opt-in for the replicated durability tier only; default concurrency model is single-writer/multi-reader. |
Consequences
- The kernel becomes smaller and more defensive. It governs; it does not remember on behalf of applications.
- TruthDB's scope narrows to system truth. Its
scopefield gainscell-localandpair-contractvalues; cell-local writes do not enter the global STL. - The Cell Contract (SPEC-171 §2) gains a
shares { ... }block declaring cross-cell domains and aclos { ... }block declaring durability defaults. - Foreign and sandboxed cells (NipCells, POSIX cells) must declare a one-time contract before sharing state. The kernel validates the contract; it does not mediate subsequent traffic.
- NexShell discovery queries a manifest layer only — domain names, not values. Sensitive cells may declare
ephemeral(manifest unpublished) oranonymous(manifest hashed, name not visible) modes. - Pact replication becomes a subscriber pattern: a forwarder service subscribes to
replicated-tier objects and propagates them. Local CLR operations never pay network latency. - SPEC-173 (Cell-Local Object Store) is the mechanism spec implementing this doctrine (ACCEPTED v1.0, 2026-07-02). K8 is the law; SPEC-173 is the engineering.
- The unikernel principle (K1) is reinforced: each cell carries its own OS logic via the Membrane, and that logic now includes the cell's own state management.
References
SPEC-140— Nexus Registry Protocol (system truth tier; cell-local tier is out of scope here)SPEC-170— TruthDB Architecture (governs system truth)SPEC-171— Universal Cell Model (cells gainsharesandclosblocks)SPEC-173— Cell-Local Object Store (the mechanism spec implementing K8)SPEC-051— Capability Algebra (contracts and capability-gated sharing)SPEC-022— ION Rings (transport for observations)SPEC-085— NexFS (persistent substrate forrestartandpersistentdurability tiers)
K9: Lessons from io_uring (ION Generation 2)
Status: Accepted
Date: 2026-07-03
Amends: K4 — ION Rings Over Pipes and Sockets, A2 — SysTable Frozen ABI
Companion spec: SPEC-022B-KERNEL-ION-OBJECT-PROTOCOL (ION v2 wire + protocol)
Companion RFC: .agents/reports/2026-07-03-0843-voxis-rfc-ion-v2-object-protocol.md
Context
Linux's io_uring proved a structural point the industry had been circling for a decade: shared-memory command queues beat synchronous syscalls on the kernel boundary. The mechanism — SQ ring, kernel consumer, CQ ring — is correct. But io_uring also became a security catastrophe. Google restricted it across its entire fleet because the opcode surface grew into an unmanageable source of kernel exploits. The reason is structural: in io_uring, every opcode is a privileged kernel entry point. The opcode table is open. Each new opcode is a new audit surface. Adding opcodes freely is exactly what produced the exploit plague.
Nexus ratified two related doctrines in 2026 that already point past this:
- K7 (Kernel Protocol Boundary, 2026-07-02) reframed the boundary as a protocol, not a syscall table: "the ABI is an implementation detail; the protocol is the contract." It explicitly committed Nexus to additive wire evolution: "new packet types by spec amendment; wire layouts are read-only after ratification."
- K8 (Cells Own Application State) stated the load-bearing invariant: "ION transports observations (events), never database queries. The kernel routes bytes; it cannot inspect payload." And: "Objects are passive. Cells execute. A cell subscribes, observes, and acts."
Despite K7/K8, the implementation specs (SPEC-022, SPEC-151) still carry POSIX-flavored opcodes: CMD_FS_OPEN, CMD_FS_READ, CMD_NET_SEND, CMD_BLK_REQ, CMD_REG_QUERY. That is the rot. The doctrine says "async object protocol"; the wire says "async syscalls." Three live drifts compound the contradiction: IonPacket is 24B in channel.zig vs 32B in SPEC-151; SysTable is 136B in SPEC-022 vs 240B in ion.zig; the channel-ID scheme differs across the public docs (0x1000+), SPEC-022 §12 (0x000–0xFFF), and SPEC-151 (0x0–0x7).
io_uring is therefore not the competitor. It is evidence — that the industry is converging on shared-memory command queues as the kernel boundary — and a cautionary tale about open opcode surfaces. The question K9 answers is how to take io_uring's structural wins without inheriting its security failure.
Decision
ION is re-specified as Generation 2 (magic 0x494F4E32 "ION2"): "io_uring for objects, capabilities, and transactions." v2 is a new generation, not a silent mutation of v1 — this honors K7's "wire read-only after ratification" by treating the v2 wire as a new ratified contract while v1 is frozen as historical record.
The v2 design separates three layers that io_uring (and POSIX) conflated:
Layer 3 — Authority: 7 capability verbs (SPEC-051, CLOSED) SPAWN SEND RECV MAP MASK TICK GRANT
Layer 2 — Interaction: 5 protocol verbs (NEW, ION v2) SUBMIT CANCEL SUBSCRIBE REPLY TRANSFER
Layer 1 — Transport: SPSC ring + ion_submission + ion_completion (NEW wire, generation 2)Adopted from io_uring: shared SQ/CQ rings · batched operations · completion-based API (Ticket + wait_multi) · multishot subscriptions (generalized IORING_ACCEPT_MULTISHOT) · registered buffers (capability-backed memory IDs; generalizes SPEC-022 §13 TextureRef) · zero-copy transfer · operation chaining / transactions (richer than io_uring linked SQEs because atomicity is SPEC-051 §8.5 bundle-validated).
Rejected from io_uring: POSIX syscall semantics (violates K8 invariant 3) · file-descriptor-centric design (violates K7; SPEC-051) · open opcode table as privileged kernel entry points (the exploit source; violates SPEC-051 §3 Law) · SQPOLL (violates K5 tickless / SPEC-021 Silence Doctrine: WFI is the only valid idle state).
The 5 protocol verbs are a discipline over SPEC-051's existing SEND/RECV/GRANT capability verbs on Channel caps — they add zero new authority surface. Semantic operations (OpenObject, PublishEvent, CommitTransaction, MirrorDomain, …) are typed BKDL-tier-2 payload families, gated by capability class, dispatched to the owning object. They are not opcodes. The kernel validates the capability and dispatches; it never interprets payload semantics.
The total privileged entry surface is 5 interaction verbs + 7 authority verbs = 12 primitives, both closed by law. The only additive surface is the operation-family payload registry — and adding a family adds a payload type, not an entry point. This is what makes the io_uring exploit class structurally impossible in ION v2, not merely policy-discouraged.
Alternatives Rejected
| Option | Why Not |
|---|---|
Keep v1 opcodes (CMD_FS_*, CMD_NET_*, CMD_BLK_*) | Contradicts K7 (protocol, not syscalls) and K8 (observations, not queries). Leaves the doctrine↔wire rot standing. |
| Reify POSIX as the native surface | Reverses SPEC-073 (Chimera bridge); POSIX is a compatibility layer, not the contract. Rejected in K7. |
| seL4-style microkernel message-passing | Adds ~1 µs overhead per message; rejected in A4. ION v2 keeps data on shared-memory rings; the kernel stays off the hot path. |
| SQPOLL (kernel polling thread) | Burns a CPU core; violates K5 (Tickless Event-Driven) and SPEC-021 (Silence Doctrine: WFI is the only valid idle state). The scheduler wakes fibers on events instead. |
| Silent mutation of v1 wire | Violates K7's "wire read-only after ratification." A generation bump (new magic ION2) is the sanctioned evolution path. |
| Add protocol verbs as new capability verbs | Violates SPEC-051 §3 Law: "The 7 Verbs are closed." The 5 protocol verbs are a discipline over existing verbs, not new authority. |
Consequences
- The io_uring exploit class is structurally impossible. The privileged entry surface is 5 closed protocol verbs + 7 closed capability verbs = 12. Operation families are additive payload, not entry points. Adding an op family does not add a kernel code path that interprets its semantics.
- K8 invariant 3 is enforced at the wire.
RegistryObserve(a subscribe/observe op family) replacesCMD_REG_QUERY. There is no opcode that encodes a database query; "observations, not queries" becomes a protocol-level discipline. - Multishot, registered buffers, and transactions are first-class. These are the io_uring structural wins, realized at object/capability abstraction. The Founder's
Subscribe { object Window, events PositionChanged }example is a literalSUBSCRIBEwithMULTISHOTflag. - v1 is frozen as historical record. SPEC-022 retains its body unchanged with a supersede banner. v1 wire (
IONR/NXUSmagic,IonPacket/CmdPacket) is frozen; no v1 mutation is permitted after v2 ratification. - EXEC-008, EXEC-007, EXEC-009, EXEC-004 must re-proof on v2 before v2 ships. These closed against v1; the v2 wire is a new contract. Re-proof is flagged in SPEC-022B §15, not executed in this doctrine pass. Implementation and re-proof belong in a dedicated per-feature lane.
- SysTable size is canonicalized at 128 bytes (neither 136 nor 240), resolving the A2 drift. Contents continue the K7-directed migration from function pointers to ring descriptors.
- K4 is amended, not superseded. K4's "ION Rings Over Pipes and Sockets" decision (the SPSC ring as sole IPC mechanism) stands. K9 replaces the opcode/wire layer on top of those rings; it does not replace the rings themselves.
- A2 is amended. The fixed-address SysTable idea survives; the canonical v2 size and contents are defined in SPEC-022B §11.
- SPEC-051 is non-breaking amended. The 7 verbs remain closed. The 5 protocol verbs are a discipline over
SEND/RECV/GRANTonChannelcaps; they add no new capability verbs. Transactions use the existing §8.5 composite-bundle grammar.
References
SPEC-022B-KERNEL-ION-OBJECT-PROTOCOL— ION v2 wire + protocol (the engineering).agents/reports/2026-07-03-0843-voxis-rfc-ion-v2-object-protocol.md— design rationale (the why)SPEC-022-KERNEL-ION-SYSTABLE— ION v1 (historical; superseded opcode/wire layer)SPEC-021-KERNEL-RUMPK-IO— Pipe/Valve, Silence Doctrine (SPSC + WFI invariants preserved)SPEC-051-SECURITY-CAPABILITY-ALGEBRA— 7 closed verbs; §8.5 composite bundlesSPEC-071-INTERFACE-BINARY-MANIFEST— BKDL (v2 payloads are tier-2 typed)SPEC-151-NIPCELL-ION-BRIDGE— NipCell bridge (amended for v2 transparency)SPEC-173-CELL-LOCAL-OBJECT-STORE— subscribe/observe/act model (the consumer ofSUBSCRIBE)- K4 — ION Rings Over Pipes and Sockets (amended)
- K5 — Tickless Event-Driven Scheduler (forbids SQPOLL)
- K7 — Kernel Protocol Boundary
- K8 — Cells Own Application State
- A2 — SysTable Frozen ABI (amended)
- A4 — No Microkernel Message-Passing
- Linux io_uring — structural inspiration
- Google's io_uring usage restrictions — empirical evidence for the opcode-surface argument