ION Rings
Generation 2 (Contract)
ION Rings are the sole inter-process communication mechanism in Nexus OS. Every message between fibers, every network packet, every storage request, every UI event — all flow through ION Rings.
ION v2 (Generation 2) is the active contract. It is io_uring for objects, capabilities, and transactions: the structural wins of shared-memory command queues, realized at the abstraction level Nexus was always aimed at. See ADR K9 — Lessons from io_uring and
SPEC-022Bfor the full rationale.
What Is an ION Ring?
An ION Ring is a lock-free, single-producer/single-consumer (SPSC) ring buffer in shared memory. It has exactly one writer and one reader. No locks. No atomic compare-and-swap. No contention.
Producer ──→ [ slot | slot | slot | slot | slot ] ──→ Consumer
↑ write_idx ↑ read_idxThe producer writes to write_idx and advances it. The consumer reads from read_idx and advances it. The two indices never collide because the ring has a fixed capacity and the producer blocks (yields) when the ring is full.
This part is unchanged from v1. What changed in v2 is what goes in the slots: typed object operations carried by a small closed protocol, not POSIX-flavored opcodes.
Why ION Rings?
Traditional IPC mechanisms have fundamental problems:
| Mechanism | Problem |
|---|---|
| Pipes | Kernel copy on every message. Syscall overhead. |
| Shared memory + mutexes | Lock contention. Priority inversion. Deadlock risk. |
| Message queues | Kernel-mediated. Allocation on send. |
| Signals | Asynchronous, hard to reason about. Race conditions. |
ION Rings solve all of these:
- Zero-copy: Data is written directly into shared memory, or referenced by a registered buffer ID. No kernel copy.
- No locks: SPSC design means no synchronization primitives needed.
- No syscalls on the hot path: Once the ring is mapped, producers and consumers operate entirely in userland.
- Bounded: Fixed-size rings prevent unbounded memory growth.
The Three-Layer Model (v2)
ION v2 separates three concerns that older designs (POSIX, io_uring) conflated under "syscall":
| Layer | Surface | Closure |
|---|---|---|
| Authority (Capability Algebra) | 7 capability verbs: SPAWN SEND RECV MAP MASK TICK GRANT | Closed |
| Interaction (this page) | 5 protocol verbs: SUBMIT CANCEL SUBSCRIBE REPLY TRANSFER | Closed |
| Transport (the wire) | SPSC ring + ion_submission + ion_completion (64B each) | Frozen at generation |
The total privileged entry surface is 5 closed protocol verbs (interaction) + 7 closed capability verbs (authority) = 12 total — but only 7 are authoritative: the 5 protocol verbs decompose into the 7 capability verbs over existing Channel caps (SPEC-022B §3.6), so the authority surface is still 7. Both layers are closed by law (SPEC-051 §3; SPEC-022B §3.4). Semantic operations (OpenObject, PublishEvent, CommitTransaction, …) are typed payload families — they extend the protocol additively without adding entry points. This is what makes the io_uring exploit class structurally impossible in Nexus: the opcode table is not open.
The Five Protocol Verbs
| Verb | Capability required | Completes | Description |
|---|---|---|---|
SUBMIT | Channel + SEND | one (or none for fire-and-forget) | Push an ion_submission onto a TX ring. Payload is a typed operation family. |
CANCEL | same Channel + SEND | one ack | Cancel a pending ticket or teardown a multishot subscription. |
SUBSCRIBE | Channel + SEND, paired Channel + RECV for the stream | many (multishot) | Register interest in a stream of events. One submission → N completions. |
REPLY | Channel + SEND | one | Push an ion_completion in response to a SUBMIT. |
TRANSFER | GRANT on the cap being moved | one ack | Move a capability (and derived buffer IDs) to another cell. |
Every protocol verb decomposes into existing capability verbs. ION v2 adds zero new authority.
What ION v2 Adopts From io_uring
- Shared SQ/CQ rings (already the ION model since v1).
- Batched operations (one
SUBMITmay carry a transaction chain). - Completion-based API — every
SUBMITreturns aTicket(u64);wait_multiblocks on tickets. - Multishot subscriptions — one
SUBSCRIBEproduces a stream of completions, torn down byCANCEL. Generalizes io_uring'sIORING_ACCEPT_MULTISHOTto semantic event streams. - Registered buffers —
RegisterBuffer(Memory|DmaWindow cap, bounds) → BufferId; submissions referencebuffer:N, not(ptr, len). Capability revoke auto-invalidates derived IDs. - Zero-copy transfer.
- Transactions — chained submissions validated atomically via capability bundles. Richer than io_uring linked SQEs because atomicity is bundle-validated.
What ION v2 Rejects From io_uring
- POSIX syscall semantics (ION transports objects/observations, never syscalls — K8).
- File-descriptor-centric design (handles are typed, scoped, revocable caps — K7).
- Open opcode table as privileged kernel entry points (the exploit source — closed by construction here).
- SQPOLL — burning a polling core violates K5 (Tickless) and the Silence Doctrine. The scheduler wakes fibers on events instead.
Well-Known Channel IDs
ION v2 channel IDs are u32, partitioned by high nibble into owner class and typed by mid bytes:
| Channel ID | Direction | Purpose |
|---|---|---|
0x01000001 | RX | Console input |
0x01000002 | TX | Console output |
0x02000001 | RX/TX | VFS / Object broker |
0x03000500 | RX | Network receive (ethertype demux'd by NetSwitch) |
0x03000501 | TX | Network transmit |
0x04000001 | RX/TX | Block Valve |
0x07000001 | RX/TX | Registry observe |
0x0D000001 | RX/TX | Display / TextureRef |
0x0F000001 | RX/TX | Control (Hello/Shutdown/Ping) |
The high nibble (0x0 kernel, 0x1 primary NipCell, 0x2–0xE multi-NipCell) gives every channel an unambiguous owner. This subsumes the v1 schemes (which disagreed across public docs, SPEC-022, and SPEC-151).
How Fibers Use ION Rings
- A fiber is spawned with a set of capability slots (CSpace, 64 slots max) validated atomically as a bundle (Capability Algebra).
- Each slot can hold a channel capability with a permission mask (
READ,WRITE, or both). - The fiber
SUBMITsion_submissionrecords onto its TX rings andRECVsion_completionrecords from its RX rings. - The kernel maps the ring's physical memory into the fiber's address space.
A fiber can only SUBMIT to rings for which it holds a Channel cap with SEND permission. Attempting to access an unauthorized ring triggers a capability fault and the fiber is killed.
Integration with NetSwitch
The Network Membrane uses ION Rings for packet delivery:
- Hardware NIC (VirtIO) delivers a raw frame to the HAL.
- The NetSwitch (kernel L2 demux) reads the EtherType.
- Based on the EtherType, the frame is placed on the correct fiber's ION Ring:
0x0800(IPv4) → Membrane fiber for LwIP processing0x88B5(UTCP) → UTCP handler fiber0x4C57(LWF) → Libertaria Wire Frame handler
- The receiving fiber processes the frame entirely in userland.
No kernel involvement after the initial demux. The kernel delivers the mail; it does not read the letter.
Capacity and Sizing
Ring capacity is configured per channel type:
- Network rings: 256 slots (high throughput)
- Console rings: 128 slots
- VFS rings: 64 slots
Slot sizes are 64 bytes (cache-line aligned) in v2, holding one ion_submission or ion_completion each. Large payloads use registered buffers or the inline payload appendix — not oversized slots.
Generation 1 (Historical)
ION v1 (SPEC-022, magic 0x494F4E52 "IONR" / 0x4E585553 "NXUS") is frozen as historical record. The v1 opcode table (CMD_FS_*, CMD_NET_*, CMD_BLK_*, CMD_REG_QUERY) and wire structs (IonPacket, CmdPacket) are superseded by v2's protocol-verb layer and ion_submission/ion_completion wire. See SPEC-022B §15 for the EXEC re-proof plan that gates v2 shipping.
References
SPEC-022B— ION Object Protocol — v2 wire + protocol (the engineering)- K9 — Lessons from io_uring — the ADR
- K4 — ION Rings Over Pipes and Sockets — the original ring decision (amended by K9)
- Kernel Protocol — the three-layer boundary doctrine
- Capability Algebra — the 7-verb authority surface