Skip to content

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-022B for 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_idx

The 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:

MechanismProblem
PipesKernel copy on every message. Syscall overhead.
Shared memory + mutexesLock contention. Priority inversion. Deadlock risk.
Message queuesKernel-mediated. Allocation on send.
SignalsAsynchronous, 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":

LayerSurfaceClosure
Authority (Capability Algebra)7 capability verbs: SPAWN SEND RECV MAP MASK TICK GRANTClosed
Interaction (this page)5 protocol verbs: SUBMIT CANCEL SUBSCRIBE REPLY TRANSFERClosed
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

VerbCapability requiredCompletesDescription
SUBMITChannel + SENDone (or none for fire-and-forget)Push an ion_submission onto a TX ring. Payload is a typed operation family.
CANCELsame Channel + SENDone ackCancel a pending ticket or teardown a multishot subscription.
SUBSCRIBEChannel + SEND, paired Channel + RECV for the streammany (multishot)Register interest in a stream of events. One submission → N completions.
REPLYChannel + SENDonePush an ion_completion in response to a SUBMIT.
TRANSFERGRANT on the cap being movedone ackMove 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 SUBMIT may carry a transaction chain).
  • Completion-based API — every SUBMIT returns a Ticket(u64); wait_multi blocks on tickets.
  • Multishot subscriptions — one SUBSCRIBE produces a stream of completions, torn down by CANCEL. Generalizes io_uring's IORING_ACCEPT_MULTISHOT to semantic event streams.
  • Registered buffersRegisterBuffer(Memory|DmaWindow cap, bounds) → BufferId; submissions reference buffer: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 IDDirectionPurpose
0x01000001RXConsole input
0x01000002TXConsole output
0x02000001RX/TXVFS / Object broker
0x03000500RXNetwork receive (ethertype demux'd by NetSwitch)
0x03000501TXNetwork transmit
0x04000001RX/TXBlock Valve
0x07000001RX/TXRegistry observe
0x0D000001RX/TXDisplay / TextureRef
0x0F000001RX/TXControl (Hello/Shutdown/Ping)

The high nibble (0x0 kernel, 0x1 primary NipCell, 0x20xE 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

  1. A fiber is spawned with a set of capability slots (CSpace, 64 slots max) validated atomically as a bundle (Capability Algebra).
  2. Each slot can hold a channel capability with a permission mask (READ, WRITE, or both).
  3. The fiber SUBMITs ion_submission records onto its TX rings and RECVs ion_completion records from its RX rings.
  4. 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:

  1. Hardware NIC (VirtIO) delivers a raw frame to the HAL.
  2. The NetSwitch (kernel L2 demux) reads the EtherType.
  3. Based on the EtherType, the frame is placed on the correct fiber's ION Ring:
    • 0x0800 (IPv4) → Membrane fiber for LwIP processing
    • 0x88B5 (UTCP) → UTCP handler fiber
    • 0x4C57 (LWF) → Libertaria Wire Frame handler
  4. 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