Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Air’s layer 0 — introduction (developers)

Overview document, to get up to speed on layer 0 quickly and know where to find the reference. The ADRs (docs/adrs/) are project-internal documents; this document is aimed at the developers who consume or study layer 0.

What layer 0 is

Layer 0 is Air’s syscall foundation: a typed, safe Rust API on top of the Linux system calls, without libc. Two crates:

  • air-sys-types — the pure types (no syscall): typed identifiers (Pid, Tid, Uid/Gid, WatchDescriptor…), Errno, flags/bitflags, mirror types (#[repr(C)]) of the kernel structures.
  • air-sys-syscall — the wrappers that call the kernel via core::arch::asm! and return Result<T, Errno>.

What you find here:

  • 11 syscall families: process, fs, mem, signal, time, net, ipc, security, system, device (uevent/evdev), ebpf (bpf() + perf_event_open);
  • the complete io_uring module (12 “Temps”: core, files, network, async, uring_cmd, registration, provided buffers, linked operations, multishot, multi-thread, confinement, raw access) — the foundation of Air’s asynchronous I/O;
  • extensions: fs::inotify, CPU affinity (sched_set/getaffinity), mem::MmapRegion, the privsep extension of process (setgroups/setres*id).

Why this architecture

  • No libc, no rustix. Layer 0 talks to the kernel directly in asm!. Goal: total control of the behavior, no C dependency, and a no_std-compatible target. (ADR-004: Linux tier-1.)
  • Safety through typing. Option<T> instead of the kernel sentinels (-1/0), Result<T, Errno> instead of the global errno, OwnedFd/BorrowedFd (RAII, no FD leak), newtypes per identifier. (Conventions carved in stone: ADR-021.)
  • io_uring as the async foundation. Air’s asynchronous runtime (ADR-023) is built on io_uring (ADR-022/028), not on epoll nor Tokio. A safe, proven “slab S1” ownership model (Miri/loom).
  • Modern variants preferred: clone3 (not clone), pidfd_* (not the raw PID), waitid (not wait/waitpid), signalfd (ADR-020), io_uring’s *2 register-ops…
  • Foundational-layer rigor: 100% coverage of lines + branches (excluding documented exceptions), property-testing, fuzzing on every parser of external data, Miri/loom on unsafe/concurrent code. (Principle 1.)

Why a 6.12 kernel minimum

6.12 is an LTS kernel (long-term support, consistent with Air’s durability horizon), and it is the version where the io_uring surface Air depends on is available (down to the recent operations and register-ops), alongside Landlock, seccomp-bpf, eBPF, etc. Targeting a single stable version avoids the hell of compatibility #ifdefs and keeps layer 0 simple and auditable. The syscall numbers and layouts are verified per architecture (x86_64 and aarch64) against the 6.12 uapi.

What you will not find here (and why)

  • No libc, no rustix — by design (see above).
  • The legacy syscalls deliberately set aside, listed and justified in UNSUPPORTED.md: the old variants superseded by a modern variant (clone, wait/waitpid, raw PIDs…), and the obsolete io_uring mechanisms (PROVIDE_BUFFERS/REMOVE_BUFFERS op-based, untagged v1 register-ops…).
  • Deferred by decision (present nowhere, and that’s intentional): epoll (the readiness need is covered by io_uring’s poll/multishot; only speculative Epoll* types exist), fanotify (privileged primitive, for a later security service), synchronous futex (will come with air-thread’s in-house futex implementation).

UNSUPPORTED.md is the reference list of these choices.

External dependencies

None in production. Layer 0 is pure Rust + asm! — no C, no libc, no rustix. The only dependencies are test-only (property-testing, loom, cargo-fuzz), isolated and subject to deny.toml/cargo audit (carve-out ADR-030). This is unusual and assumed: the foundation depends on nothing above the kernel.

For the C/C++ developer

Layer 0 does not expose a C ABI, and that is intentional: all its value lives in the Rust type system (RAII, lifetimes, Option/Result, io_uring ownership) — which does not cross a C boundary. If you are in C/C++:

  • raw syscalls → use your libc (glibc/musl); io_uring in Cliburing;
  • Air features → the C ABI of layers 1+ (air-base-lib, then air-runtime/air-aircom), where Air is polyglot by design (ADR-027);
  • building C tools on top of the Air stack is the subject of a libc-compat track (exploration, docs/notes/), not of layer 0.

Reference — to go further

  • Per-family specs: docs/specs/layer-0/family-*.md.
  • io_uring module: master inventory io-uring-0-inventaire.md + one document per Temps (io-uring-1-core.mdio-uring-4-raw.md).
  • Decisions: ADR-021 (layer 0 conventions), ADR-019 (errors), ADR-020 (signals), ADR-022/028 (io_uring), ADR-029 (naming), ADR-024/030/034 (dependencies), ADR-031/035 (coverage & exception taxonomy).
  • Reference lists: UNSUPPORTED.md (legacy set aside), COVERAGE-EXCEPTIONS.md (justified coverage exceptions).
  • API: the generated rustdoc reference (/api/).

Document license: MPL 2.0.