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 — Macro-architecture

Document fondateur — Version 1.2 (2026-07-10 : passe post-M5 + révision doctrinale — Air = userland Rust safe pour Linux 6.12+ (pas POSIX) ; unsafe en couche 0 seulement ; stabilité par couche (0/1 internes, couche 2 = contrat public) ; surfaces libair (Rust) / libair_c (libc) / ABI C objet ; Managers de domaine [ADR-077] ; cible air-sshd [ADR-074])

Préambule

À qui s’adresse ce document

Ce document décrit l’architecture logicielle d’Air, environnement de bureau pour systèmes Linux, à un niveau de détail intermédiaire entre la Vision (qui pose le pourquoi et le quoi général) et les spécifications de composants (qui détailleront chaque crate, chaque API, chaque protocole).

Il est destiné principalement aux ingénieurs qui contribueront au code d’Air, aux développeurs qui voudront écrire des applications natives .airapp ou des services .airservice, et aux mainteneurs de distributions Linux qui voudront comprendre comment packager Air. Il suppose une familiarité avec les concepts Unix modernes, le langage Rust, le protocole Wayland, et l’écosystème Linux desktop contemporain. Il n’est pas un manuel d’introduction à ces sujets.

Comment lire ce document

La section 1 (Vue d’ensemble) donne le panorama global. Elle peut être lue isolément pour comprendre l’architecture d’Air à grands traits.

Les sections 2 à 7 détaillent chacune une couche : périmètre, composants internes, contrats exposés vers le haut et consommés vers le bas, choix techniques clés. Elles peuvent être lues séquentiellement (bottom-up, cohérent avec la philosophie de construction) ou consultées indépendamment selon les besoins.

La section 8 traite les aspects transverses (sécurité, internationalisation, accessibilité, observabilité, énergie, confidentialité, stabilité) qui ne tiennent pas dans une couche unique. La section 9 formalise les contrats inter-couches et les règles d’isolation. La section 10 traite l’évolution dans le temps et la stabilité des contrats.

Conventions

Les noms de composants Air sont préfixés par air- quand ils sont nommés concrètement (air-wm, air-com, air-object). Les concepts génériques restent en français ou en anglais selon le terme le plus précis dans le domaine (compositeur, sandbox, capability).

Les références aux ADRs prennent la forme ADR-NNN et désignent les Architecture Decision Records du registre fondateur d’Air. La connaissance des ADRs est supposée acquise ; ce document les met en cohérence sans les répéter.

Le présent document est sous licence MPL 2.0, comme l’ensemble du projet Air.


Section 1 — Vue d’ensemble du stack

Doctrine fondatrice — ce qu’Air est. Air est le userland Rust safe du kernel Linux. Pas « un OS POSIX de plus » : Air vise Linux, pas POSIX (cf. ADR-004, ADR-046) — conformité fonctionnelle au kernel, pas conformité de certification. Quatre invariants en découlent, valables sur tout le stack :

  1. Linux-only, définitif. Kernel Linux 6.12 (LTS) minimum. Aucune vocation à un autre kernel ; aucun fallback FreeBSD/Windows/macOS — l’architecture épouse Linux.
  2. unsafe en couche 0 uniquement (étoile polaire). L’interface avec le kernel est le seul endroit où du unsafe Rust doit exister — c’est la barrière de sécurité. Partout au-dessus : Rust safe, garanti safe. Là où du unsafe subsiste encore au-dessus (bootstrap/TCB d’air-runtime, GlobalAlloc d’air-alloc, frontière C des toits libc), c’est un site nommé, audité, minimal, traité comme dette à faire descendre/éliminer.
  3. Les couches sont un rempart devant le kernel. Elles filtrent et détectent au plus tôt ce qui serait transmis au kernel comme non conforme : Air protège le kernel des imperfections des applications (Principe d’ingénierie 4, validation en amont).
  4. Sens montant : zéro masquage, zéro transformation. Ce que le kernel retourne, Air le restitue fidèlement en l’habillant d’idiomes Rust safe (types, Result, Option, RAII). Les API collent aux features et au mode de fonctionnement du kernel — Air n’adapte pas, ne réinvente pas une sémantique : « le kernel est la référence ».

Le principe de l’architecture en couches

Air est organisé en six couches logicielles superposées, numérotées de 0 (la plus basse, au contact du kernel) à 5 (la plus haute, au contact de l’utilisateur). Chaque couche a une responsabilité claire et un contrat stable avec ses voisines immédiates. Cette organisation suit une discipline stricte : une couche ne peut consommer que les services de la couche immédiatement inférieure, sauf cas exceptionnels nommés et justifiés. Cette discipline garantit que la complexité reste localisée et que chaque couche est remplaçable ou évolutive indépendamment des autres.

La séparation en couches n’est pas qu’un découpage conceptuel : elle se matérialise dans le packaging, les processus, et les contrats publics d’Air. Une couche basse compile et tourne sans connaître l’existence des couches supérieures. Une couche haute consomme la couche basse via des API stables sans présumer de ses détails d’implémentation. Cette propriété rend l’architecture compréhensible, testable couche par couche (cf. Principe d’ingénierie 1), et évolutive sans casser les contrats existants (cf. ADR-012).

Les six couches en bref

┌─────────────────────────────────────────────────────────┐
│  Couche 5 — Cycle de vie applicatif et services système │
│   (air-launchd, air-registry, air-trust,                │
│    air-firewalld, air-printd, air-bluetoothd, ...)      │
├─────────────────────────────────────────────────────────┤
│  Couche 4 — Frameworks applicatifs                      │
│   (air-ui pour graphique, air-tui pour console)         │
├─────────────────────────────────────────────────────────┤
│  Couche 3 — Composition, rendu, audio, input            │
│   (air-wm compositeur Wayland, air-console mode texte,  │
│    rendu Vello/Cosmic Text, intégration PipeWire)       │
├─────────────────────────────────────────────────────────┤
│  Couche 2 — Modèle d'objet, IPC, services fondamentaux  │
│   (air-object modèle C-ABI, air-com IPC,           │
│    intégration systemd/udev, services de base)          │
├─────────────────────────────────────────────────────────┤
│  Couche 1 — Primitives système                          │
│   (processus, threading, mémoire, sockets, crypto,      │
│    accès devices, exposée Rust + C ABI)                 │
├─────────────────────────────────────────────────────────┤
│  Couche 0 — Abstraction kernel Linux                    │
│   (façade Rust : syscalls, io_uring, Landlock,          │
│    seccomp, namespaces, Unix sockets, perf_event)       │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
                    Kernel Linux 6.12+ (LTS) + systemd

Le schéma ci-dessous matérialise les crates existants dans chaque couche (couche 0 scellée couche-0-v1.12, couche 1 scellée couche-1-v1.0 → re-scellée additivement jusqu’à couche-1-v1.9, les toits — libc C-ABI et PAL Rust std::sysen production ; couches 3 à 5 à venir) :

Architecture Air — les 6 couches avec les crates existants

🎉 Jalon toolchain (M5, 2026-07-10) — std Rust tourne sur la libc d’Air, zéro glibc. Un programme std (hello-std, static-PIE) lié intégralement contre la libc d’Air (libair_c, les crates air-libc-* — à ne pas confondre avec libair, la lib Rust de la couche 2) s’exécute on-target sur les 2 arches (x86_64-air et aarch64-air). Les deux toits de la couche 1 sont donc prouvés : la libc C-ABI (libair_c, ~256 symboles, la face C) et le PAL Rust (std::sys de la cible *-linux-airADR-076, ADR-075). Ces deux toits ne consomment jamais la couche 0 en direct : ils bindent la couche 1 via des Managers de domaine objets (ADR-077 : AirFileManager, AirTaskManager, AirNetworkManager, AirSystemManager, AirSignalManager, AirEnvironmentManager, AirMemoryManager). Prochaine cible de production : air-sshd — le serveur SSH d’Air full Rust, async io_uring, wire-compat OpenSSH (ADR-074) ; compiler OpenSSH n’aura été qu’une fonction de forçage de la libc.

Couche 0 — Abstraction kernel Linux. Façade Rust propre sur les services du kernel. Encapsule les appels système, l’I/O asynchrone via io_uring, les primitives de sandboxing (Landlock, seccomp, namespaces, cgroups), la communication inter-processus via Unix sockets, l’accès aux devices via netlink et sysfs, l’observabilité kernel via eBPF et perf_event. Cette couche abstrait le kernel sans le cacher : un développeur système Linux retrouve ses repères. Elle est volontairement fine, conçue pour être testable à 100 % et auditable en intégralité.

Couche 1 — Primitives système. Fournit les briques fondamentales que toute couche supérieure consomme : gestion des processus et du threading, allocation mémoire structurée, primitives I/O synchrones et asynchrones unifiées, sockets BSD enrobés en API Rust idiomatique, primitives cryptographiques de base, accès aux devices via udev. Cette couche est écrite en Rust pur et exposée également en ABI C pour les consommateurs polyglottes. Elle vise un coverage de tests de 100 % (cf. Principe d’ingénierie 1).

Couche 2 — Modèle d’objet, IPC, services fondamentaux. Le cœur conceptuel d’Air. Elle porte le modèle d’objet C-ABI (air-object) qui donne à toute classe une identité runtime, des propriétés observables, et la bindabilité polyglotte (cf. ADR-002). Elle porte AirCom (air-com), le protocole IPC capability-based d’Air (cf. ADR-001). Elle fournit l’event loop unifié, les collections Unicode-aware, le système de notification, l’intégration avec systemd via sd-bus peer-to-peer, le pont D-Bus optionnel, et les services fondamentaux que toute couche supérieure consomme. Cette couche est le lieu où le système devient cohérent : tout ce qui est au-dessus parle le langage du runtime d’objets et de AirCom.

Couche 3 — Composition, rendu, audio, input. Prend en charge la mise à l’écran et la mise en son. Elle inclut deux compositeurs jumeaux qui partagent leur backend : air-wm pour le mode graphique Wayland (cf. ADR-003), et air-console pour le mode texte avec bypass de la VT kernel (cf. ADR-007). Elle inclut le rendu graphique 2D via Vello (GPU-first), le rendu de texte avec support bidirectionnel via Cosmic Text et HarfBuzz, et l’intégration PipeWire pour l’audio. Le backend matériel (DRM/KMS, evdev, seats via logind, glyph rasterizer) est partagé entre air-wm et air-console, ce qui garantit la cohérence des deux modes.

Couche 4 — Frameworks applicatifs (développeur : UI + fondation). Fournit aux développeurs d’applications les outils pour écrire des applications — frameworks d’interface et commodités de fondation (périmètre élargi par ADR-084, sur le modèle de Foundation d’Apple qui coexiste avec UIKit). Côté UI, deux frameworks symétriques : air-ui pour le mode graphique (cf. ADR-009), et air-tui pour le mode texte (cf. ADR-008). Les deux partagent un modèle de composition de vues déclaratif transposé en Rust idiomatique, avec une emphase sur l’explicitation du flux de données (Principe d’ingénierie 7). Widgets atomiques et vues composites de haut niveau (panneaux de paramètres, calendriers, alertes, etc.). Une application peut viser un mode unique ou les deux modes simultanément, avec capability-gating pour les fonctionnalités spécifiques à un mode. Côté fondation, les commodités applicatives bâties sur les services système — dont air-url (chargement d’URL haut niveau ≈ URLSession, sur air-http/air-network de la couche 2, cf. ADR-084).

Couche 5 — Cycle de vie applicatif et services système. Englobe tout ce qui orchestre la vie des applications et fournit les services transverses du système. air-launchd lance les applications avec leurs entitlements et sandbox (cf. ADR-010), air-registry gère la découverte des services AirCom (cf. ADR-001), air-trust vérifie les signatures et notarizations (cf. ADR-010, ADR-015). Les services système comme air-notifyd (notifications), air-firewalld (FireWall, cf. ADR à produire — air-firewalld), air-power (énergie, cf. ADR à produire — air-power), air-printd (impression, cf. ADR à produire — sous-systèmes hardware), air-bluetoothd (Bluetooth, cf. ADR à produire — sous-systèmes hardware), air-share (partage entre appareils, cf. ADR à produire — air-share) vivent ici. air-shell (shell graphique en mode air-desktop) est implémenté comme .airapp particulière avec entitlements étendus. Cette couche est la plus diverse et la plus visible côté utilisateur final.

Une couche transverse implicite : systemd et le kernel

Au-dessous de la couche 0 se trouvent le kernel Linux et systemd. Ils ne sont pas une couche Air à proprement parler — Air ne les développe pas, mais s’appuie dessus comme un socle stable.

Le kernel Linux fournit les services standards (gestion des processus, mémoire, FS, réseau, drivers, etc.) que la couche 0 d’Air encapsule. Air est Linux-only, définitivement (cf. ADR-004) : il n’a aucune vocation à être porté sur un autre kernel. Ce n’est pas une réserve prudente mais une décision fondatrice — l’architecture entière (io_uring, Landlock, seccomp-bpf, eBPF, namespaces/cgroups, clone3, pidfd) épouse le kernel Linux et n’a de sens que sur lui. La couche 0 n’est pas une couche de portabilité multi-OS ; c’est la façade Rust de Linux. Version kernel Linux minimale requise : 6.12 (LTS) — io_uring mature, Landlock, cgroups v2, eBPF complet. Aucun fallback vers les kernels antérieurs.

systemd est traité comme un socle système d’Air (cf. ADR-005). Air consomme systemd via sd-bus en mode peer-to-peer (pas via le broker D-Bus), avec sd-event, sd-notify, sd-journal, libudev, logind, et systemd --user. Cette intégration est dure : Air ne fonctionne pas sans systemd. Les composants Air sont packagés comme units systemd (system pour les .airservice, user pour les .airapp), ce qui leur donne supervision, socket activation, hardening, et logging structurés gratuitement.

Le mode console et le mode graphique : deux profils, une architecture

Air supporte deux profils de fonctionnement (cf. ADR-006) qui se distinguent par la présence ou l’absence des couches 3 et 4, mais qui partagent intégralement les couches 0, 1, 2, et une partie de la couche 5.

air-base est le profil console. Il contient les couches 0, 1, 2 complètes, et la partie non-graphique de la couche 5 (services système comme air-notifyd, air-firewalld, air-power, etc.). Il permet d’exécuter des applications console (CLI tools, TUI apps, daemons) et de faire fonctionner un système Linux complet sans environnement graphique. Aucun composant air-desktop n’est requis ni installé en air-base.

air-desktop est le profil graphique. Il étend air-base avec les couches 3 (compositeur Wayland air-wm, audio, rendu) et 4 (framework UI graphique air-ui), et les composants couche 5 spécifiques au desktop (shell graphique, panneau de préférences, applications graphiques).

Cette séparation est un invariant architectural, pas un détail de packaging. Le mode console est de première classe (cf. ADR-007) et bénéficie de son propre compositeur air-console qui contourne les limitations de la VT kernel pour offrir aux applications TUI un accès réel aux événements d’entrée et un rendu pleine maîtrise. Le framework air-tui permet d’écrire des applications console modernes avec un modèle déclaratif comparable à celui d’air-ui.

Sur une machine, on installe air-base seul (typiquement serveur, conteneur, machine headless) ou air-base + air-desktop (machine utilisateur classique). Il n’existe pas de configuration où une couche supérieure est requise par une couche inférieure.

Cohérence verticale : le modèle d’objet et AirCom comme colonne vertébrale

Trois propriétés se diffusent verticalement à travers toutes les couches d’Air et lui donnent sa cohérence.

Le modèle d’objet C-ABI (cf. ADR-002) introduit en couche 2 est consommé par les couches 3, 4 et 5. Une AirView en couche 4 est un AirObject qui vit dans le runtime de la couche 2. Une fenêtre air-wm en couche 3 est un AirObject. Un service AirCom en couche 5 expose des AirObject. Cette uniformité signifie qu’un outil d’introspection, un debugger, un binding polyglotte, ou un lecteur d’écran (cf. ADR-017) peut inspecter, manipuler, ou observer n’importe quel objet vivant dans Air via une seule API runtime. Aucun composant Air ne réinvente son propre système d’objets, et aucun langage tiers n’a besoin d’écrire des bindings spécifiques par classe.

AirCom (cf. ADR-001) est l’IPC universel d’Air. Toute communication inter-processus entre composants Air passe par AirCom. La couche 2 fournit le transport et le runtime ; les couches 3 à 5 exposent leurs services et consomment ceux des autres via AirCom. air-wm expose air.wm.accessibility, air.wm.clipboard, et consomme air-launchd via AirCom ; air-notifyd expose air.system.notifications, consommé par les apps .airapp ; et ainsi de suite. Cette uniformité signifie qu’un service Air, qu’il soit en couche 5 (système) ou consommé par une application en couche 4, est conçu et utilisé selon les mêmes patterns capability-based.

Le sandboxing capability-based (cf. ADR-010) introduit en couche 5 (via air-launchd et les entitlements) s’appuie sur AirCom en couche 2 et sur les primitives de sandboxing (Landlock, namespaces, seccomp) en couche 0. Une application reçoit au démarrage un ensemble initial de capabilities AirCom et un environnement isolé par namespaces. Elle ne peut consommer que les services dont elle a déclaré la capability dans son manifeste, et ne peut accéder qu’aux ressources filesystem que son entitlement Landlock autorise. Aucune autorité ambiante. Aucun moyen pour une app malveillante de “découvrir” des services qu’elle n’a pas le droit de consommer.

Cohérence horizontale : les services système comme citoyens de première classe

Outre la cohérence verticale, Air a une cohérence horizontale au niveau de la couche 5 : tous les services système d’Air (.airservice) suivent le même pattern. Ils sont packagés en bundles signés avec entitlements (cf. ADR-010), supervisés par systemd (cf. ADR-005), cloisonnés en utilisateurs dédiés non-privilégiés (cf. Principe d’ingénierie 10), exposent leurs APIs via AirCom, et sont éventuellement remplaçables par l’utilisateur dans les limites du raisonnable.

Cette uniformité a des conséquences pratiques importantes. Un développeur qui sait écrire un service Air sait écrire n’importe lequel. Un administrateur système qui sait inspecter un service Air sait inspecter tout l’éco-système. Un utilisateur qui voit la liste des services tournants comprend que chacun a un périmètre limité et auditable. Pas de “service magique” qui aurait des privilèges spéciaux invisibles.

Apps natives, apps tierces, apps console : trois publics distincts

Air accueille trois catégories d’applications avec des niveaux d’intégration différents.

Les applications natives .airapp sont écrites avec le SDK Air (couches 1, 2, 4) et distribuées comme bundles signés (cf. ADR-010). Elles consomment intégralement le modèle d’objet C-ABI, AirCom, et les frameworks Air. Elles bénéficient de l’accessibilité par construction (cf. ADR-017), de l’i18n native (cf. ADR-016), de la sandbox capability-based, et de l’intégration enrichie avec air-wm via le protocole privé AirCom (cf. ADR à produire — protocole privé AirCom air-wm ↔ apps). Elles tournent aussi sous d’autres compositeurs Wayland avec qualité de base élevée mais sans les enrichissements.

Les applications tierces (Flatpak, GTK, Qt) tournent sous air-wm comme clients Wayland standards. Elles bénéficient des standards freedesktop (xdg-shell, xdg-desktop-portal pour les capacités) mais n’ont pas accès aux services AirCom privés d’Air. Air les supporte de première classe comme stratégie transitoire pendant la phase d’incubation où l’écosystème natif .airapp se construit (cf. ADR-013). Elles cohabitent avec les apps natives sans conflit.

Les applications console tournent en air-base (ou en mode console sous air-desktop). Si elles sont écrites avec le SDK Air et air-tui, elles bénéficient de l’intégration native (modèle d’objet, AirCom, sandbox, capability-gating). Si elles sont des programmes Unix classiques (vim, htop, ssh), elles tournent normalement dans un terminal Air ou sur un TTY géré par air-console, sans intégration spécifique mais sans non plus aucun obstacle.

La discipline d’isolation entre couches

Une couche supérieure ne peut consommer une couche inférieure que via les contrats publics exposés par celle-ci. Les détails d’implémentation d’une couche basse ne sont jamais exposés vers le haut. Cette discipline a plusieurs conséquences concrètes.

Pas d’accès direct au kernel depuis les couches supérieures à 0. Une application native n’appelle pas directement open(), read(), mmap(). Elle utilise les APIs de la couche 1 (AirFile, AirMemory), qui elles-mêmes utilisent la couche 0. Si un jour Air doit changer la façon dont la couche 0 parle au kernel (par exemple, basculer entièrement sur io_uring là où aujourd’hui il y a encore des syscalls bloquants), les couches supérieures ne le voient pas.

Pas d’invocation directe de systemd ou de D-Bus depuis les couches supérieures à 2. Une application native n’utilise pas sd-bus directement. Elle consomme AirCom, qui en interne peut parler à systemd via sd-bus pour ses besoins propres. Si Air doit un jour réduire sa dépendance à systemd ou la remplacer (peu probable mais pas impossible sur 20 ans), les couches supérieures ne le voient pas.

Pas de couplage applicatif au compositeur spécifique. Une application native parle à air-ui qui dialogue avec air-wm via Wayland et AirCom. L’application ne sait pas qu’il s’agit d’air-wm plutôt que de Mutter — elle consomme les capabilities exposées par air.wm.session (cf. ADR à produire — protocole privé AirCom air-wm ↔ apps) et dégrade gracieusement quand elles ne sont pas présentes.

Les exceptions sont nommées et justifiées. Certains composants couche 5 (notamment les .airservice système comme air-power) ont des besoins qui les amènent à consommer la couche 0 directement (accès sysfs, netlink pour la gestion énergie ou réseau, perf_event pour la mesure de consommation). Ces accès directs sont autorisés mais explicités dans la documentation du composant et soumis à audit. Ils ne sont jamais le cas général.

Les artefacts livrés par chaque couche

Pour conclure cette vue d’ensemble, voici ce que chaque couche produit comme artefacts livrables. Le détail figure dans les sections suivantes.

CoucheCrates Rust principalesBibliothèques C exposéesDaemons / servicesOutils CLI
0air-sys-types, air-sys-syscall
1air-base-core/air-base-lib, air-socket, air-crypto, air-filesystem, air-process, air-thread, air-memory, air-alloc, air-terminal, air-runtime (ADR-052)… + Managers de domaine (ADR-077)(interne, pas d’ABI publique)air-fs, air-network-tools
Toits /L1air-libc-* (libc) · PAL std::syslibair_c (libc C, Linux-conforme, zéro glibc)
2air-object, air-com, air-event, air-systemd, air-collectionslibair (surface Rust pur) · libair-object.so, libair-com.so, libair-event.soair-registryair-call
3air-wm, air-console, air-render, air-audio, air-display, air-seatlibair-render.soair-wm, air-console, air-pipewire-bridge
4air-ui, air-tui, air-ui-core, air-view-core, air-ui-widgets, air-ui-viewslibair-ui.so, libair-tui.so
5air-launchd-lib, air-power-lib, etc.air-launchd, air-notifyd, air-firewalld, air-power, air-printd, air-bluetoothd, air-share, air-nfcd, air-trust, air-appdb, air-shell, xdg-desktop-portal-airair-launch, air-trust-tool, divers

Surfaces publiques, en un coup d’œil. Les couches 0 et 1 sont internes (elles suivent le kernel, ne sont pas un contrat pour les développeurs tiers — Section 10). Ce que les développeurs consomment :

  • libair — la lib Rust pur d’Air, surface publique stable de la couche 2 (le contrat d’API app/service, garanti 10 ans, ADR-012).
  • libair_c — la libc C (crates air-libc-*), Linux-conforme (pas POSIX), que lient le C et le std Rust de *-linux-air — c’est un toit sur la couche 1, zéro glibc.
  • les ABI C du modèle d’objet de la couche 2 (libair-object.so, libair-com.so, libair-event.so) pour les bindings polyglottes — livrées en librairies indépendantes (granularité, taille d’exécutable), jamais une méga-lib unique.

Les sections 2 à 7 détaillent chaque couche, ses composants internes, ses contrats publics, ses choix techniques structurants, et ses choix laissés ouverts pour la spec future.


Section 2 — Couche 0 : Abstraction kernel Linux

Rôle et périmètre

La couche 0 est la frontière entre Air et le kernel Linux. Son rôle est de fournir aux couches supérieures un accès aux services du kernel via une façade Rust propre, sûre, et testable. Elle abstrait sans cacher : un développeur qui connaît Linux retrouve ses repères dans les noms et les concepts, mais bénéficie des garanties de typage et de sûreté mémoire de Rust.

La couche 0 ne fait pas de logique applicative. Elle ne décide rien au-delà de ce qui est nécessaire pour offrir un binding propre. Elle ne maintient pas d’état global. Elle ne consomme aucune autre couche d’Air — par définition, elle est la plus basse. Elle n’a pas de libc externe pour dépendance : depuis ADR-048, la couche 0 est no_std et émet ses appels système directement (instruction syscall/svc 0 en assembleur, pas de binding glibc/musl). C’est ce qui permet à Air de tourner sans aucune glibc (jalon M5 : std s’exécute sur notre libc libair, elle-même bâtie sur cette couche 0). Ses rares dépendances externes se limitent à quelques crates Rust très matures de wrapping, sous la règle des 80 % du Principe d’ingénierie 6.

Son périmètre couvre :

  • Appels système vers le kernel : ouverture/lecture/écriture de fichiers, gestion de processus, signaux, manipulation de la mémoire, gestion du temps.
  • I/O asynchrone moderne via io_uring (ADR-004 acte Linux tier-1 exclusif, donc io_uring assumé).
  • Sandboxing : namespaces, seccomp-bpf, Landlock, cgroups v2.
  • Communication inter-processus bas niveau : Unix sockets (AF_UNIX), pipes, eventfd, signalfd, memfd_create.
  • Accès aux devices : netlink pour NETLINK_KOBJECT_UEVENT (notification hotplug), sysfs (lecture d’attributs devices), evdev (/dev/input/eventX pour les entrées clavier/souris/touch).
  • Observabilité kernel : eBPF pour l’instrumentation système (consommé par les outils d’audit et de profiling Air, pas exposé tel quel aux apps), perf_event_open pour la collecte d’événements de performance (consommé notamment par air-power, cf. ADR à produire — air-power).

Tout ce qui n’est pas dans ce périmètre relève des couches supérieures. Notamment : la gestion de processus politique (priorisation, isolation décidée selon entitlements) relève d’air-launchd en couche 5 ; la couche 0 fournit juste fork, exec, setns, unshare, etc., en bindings sûrs.

Crates de la couche 0 : air-sys-types et air-sys-syscall

La couche 0 est livrée en deux crates Rust complémentaires. Ce découpage a été acté pendant l’instruction des spécifications phase 0 (cf. les specs détaillées dans docs/specs/layer-0/).

  • air-sys-types : crate des types fondamentaux partagés par toute la couche 0 et exposés vers les couches supérieures. Newtypes pour les ressources kernel (OwnedFd, BorrowedFd, Pid, Tid, Uid, Gid, etc.), types d’erreur (Errno), structures de données partagées entre familles. Aucune dépendance vers air-sys-syscall (la direction est inverse). Périmètre détaillé dans docs/specs/layer-0/air-sys-types.md.
  • air-sys-syscall : crate des wrappers de syscalls. Consomme air-sys-types. Organisée en sous-modules par famille fonctionnelle, chaque famille étant spécifiée dans son propre document docs/specs/layer-0/family-*.md.

Les familles actuellement spécifiées dans la phase 0 sont :

air-sys-syscall/
├── process/      — getpid, clone3, waitid, pidfd_*, prctl par opération, capabilities (capget/capset)
├── fs/           — openat2 et variantes *at, read/write, statx, mkdirat, fcntl par opération, watchers (inotify/fanotify)
├── mem/          — mmap, mprotect, madvise, memfd_create, mlock/mlock2
├── signal/       — signalfd (par défaut), masques, sigaction restreint aux 4 signaux synchrones fatals (cf. ADR-020)
├── time/         — clock_gettime, timerfd, clock_nanosleep (sans notion de fuseau horaire — relève d'icu4x, consommé dès la COUCHE 1 par air-base-lib ET la libc Air, cf. ADR-053 ; surfacé plus haut aussi)
├── net/          — socket, accept4, send/recv, sockopt typés (pas d'API "Connection" haut niveau — relève du futur framework networking couche 2)
├── ipc/          — eventfd, pipe2, splice/tee/vmsplice
├── security/     — seccomp_load_filter, Landlock (filtres déclaratifs compilés en BPF en interne)
├── system/       — uname, sysinfo, getrandom, gethostname (`getrandom` est la primitive cryptographique exposée)
└── io_uring/     — voir ci-après, fait l'objet d'un module dédié et d'un ADR propre (ADR-022)

Trois périmètres mentionnés dans les esquisses early-stage de la couche 0 ne font pas encore l’objet de specs phase 0 et sont en attente d’instruction :

  • Énumération et hotplug devices (netlink uevent, sysfs, evdev) : périmètre couche 0 par nature, à spécifier ultérieurement. Le compositeur graphique air-wm et le compositeur console air-console (cf. ADR-007) consommeront ce module quand il sera produit.
  • eBPF et perf_event_open : périmètre couche 0 par nature, à spécifier ultérieurement. Consommé par les services système d’observabilité et de monitoring d’énergie (cf. couche 5).
  • Bibliothèque cryptographique applicative : hors couche 0 par décision (RustCrypto et équivalents arrivent en couche 1+).

Le module io_uring

io_uring est traité comme un module à part entière de air-sys-syscall, distinct des autres familles à cause de son importance stratégique et de la complexité de son API. L’ADR-022 consigne 10 décisions structurantes sur ce module, et la spec détaillée est découpée en 4 Temps (Temps 1 cœur API, Temps 2 opérations FS/réseau/async, Temps 3 registration/linked/multishot/shared, Temps 4 raw). Voir docs/specs/layer-0/io-uring-overview.md et l’ADR-022.

Pour les opérations couvertes à la fois par les wrappers synchrones (net, fs, process…) et par io_uring, les deux chemins coexistent avec types et conventions partagés (Décision 2 d’ADR-022). Les couches supérieures choisissent selon le contexte (sync pour les chemins simples, io_uring pour la performance async).

Socle de bindings : syscalls directs (décision actée), rustix écarté

L’écosystème Rust dispose de plusieurs crates pour parler à la libc et au kernel Linux : libc (bindings bruts, peu sûrs), nix (bindings sûrs mais avec des choix de design vieillissants), et rustix (bindings sûrs, modernes, optionnellement sans dépendance à la libc, soutenu activement par l’équipe Bytecode Alliance).

Trace historique. rustix avait été évoqué comme candidat principal lors de la rédaction early-stage de ce document (API moderne, mode linux_raw, large couverture). Cette piste a depuis été abandonnée.

Décision actée (2026-05-31) : pas de socle de bindings externe. La couche 0 (air-sys-types + air-sys-syscall) appelle les syscalls Linux directement via core::arch::asm!, sur x86_64 et aarch64, sans aucune dépendance externe (ni rustix, ni nix, ni libc dans le chemin d’appel). Ce choix maximise le contrôle, le déterminisme de build (ADR-025) et la compatibilité no_std future, au prix d’un wrapping manuel assumé. Les opérations avancées de bas niveau (io_uring, Landlock) suivent la même règle : wrapping manuel des syscalls, pas de crate de bindings tierce. Le choix d’un éventuel binding eBPF haut niveau (libbpf-rs vs aya) reste une décision séparée, à instruire quand la famille ebpf sera spécifiée (cf. « Choix laissés ouverts »).

Gestion d’erreur : modèle hybride à deux niveaux (ADR-019 ; face ABI C : ADR-045)

Conformément à l’ADR-019, la couche 0 utilise un type d’erreur minimaliste Errno :

  • #[repr(transparent)] sur NonZeroI32 — sans allocation, sans contexte, sans chaîne de causalité.
  • Énumération des codes errno standards de Linux (~140 constantes : EBADF, ENOENT, EINTR, etc.).
  • Implémente core::error::Error pour permettre la chaîne via source() aux couches supérieures.

Aucun type d’erreur enrichi en couche 0. L’enrichissement (contexte, chemins, chaînes de causalité) est l’affaire des couches 1+ qui définissent leurs propres types Error spécifiques au domaine, encapsulant l’Errno source via #[from] pour permettre la propagation idiomatique avec ?.

Aucun panic!() dans le code public (cf. Principe d’ingénierie 4).

Conventions transverses (ADR-021)

L’ADR-021 acte cinq conventions de design qui s’appliquent à toutes les familles de la couche 0 :

  1. Préférer les variantes *at avec base explicite (openat2, mkdirat, unlinkat, etc.) plutôt que les variantes implicites. La base de chemin est un DirFd::Cwd ou un FD ; cela élimine les courses TOCTOU et clarifie les contextes d’accès.
  2. Remonter EINTR à l’appelant plutôt que de re-tenter en interne. La décision de re-tenter ou non relève des couches supérieures qui ont le contexte sémantique.
  3. Décomposer les syscalls multiplexés (prctl, fcntl, ioctl) en fonctions dédiées par opération avec types appropriés. Pas de wrapper générique qui prend un code d’opération entier.
  4. Sentinelle None pour les patterns kernel à valeur magique (signal 0 pour test d’existence, PID 0 pour processus courant, etc.). Option<T> plutôt que valeur entière sentinelle.
  5. Constantes typées et bitflags pour les drapeaux de syscalls. Pas de constantes i32 bare exposées dans l’API publique.

Ces cinq conventions structurent le design de chaque famille de wrappers.

Sûreté mémoire et zones unsafe

Doctrine — l’unsafe vit en couche 0, et nulle part ailleurs (étoile polaire). La couche 0 est l’interface avec le kernel : c’est la barrière de sécurité du userland, et donc le seul endroit où du unsafe Rust doit exister. air-sys-syscall contient du code unsafe par nécessité, sous discipline stricte : tout bloc unsafe documenté avec un commentaire // SAFETY: (vérifié en CI), API publique sans unsafe exposé sauf cas justifié (par exemple unsafe fn from_raw_fd), code unsafe concentré dans des modules dédiés audités plus strictement. Au-dessus de la couche 0, le code Rust est safe et garanti safe.

Cette règle est une étoile polaire, pas un état déjà atteint partout : quelques sites au-dessus de la couche 0 portent encore du unsafe par construction — le bootstrap / TCB / TLS / relocation d’air-runtime (couche 1), l’implémentation de GlobalAlloc par air-alloc (couche 1), et la frontière C (extern "C" / #[no_mangle]) des toits libc (le C n’offre aucune sûreté ; la logique derrière le shim reste du Rust safe couche 1). Chacun de ces sites est nommé, audité, minimal, et traité comme une dette à faire descendre en couche 0 ou à éliminer. Dette explicite ouverte : rendre GlobalAlloc safe dans le cas d’Air, en travaillant avec l’équipe libs de Rust — au même titre que le retrait de l’escape hatch raw_syscall (ADR-087).

Tests : coverage 100 % obligatoire

Cf. Principe d’ingénierie 1. Stratégies : tests unitaires par API, tests d’intégration en environnement contrôlé (containers/sandboxes), property-based testing via proptest pour APIs à paramètres complexes (notamment seccomp), fuzzing via cargo-fuzz pour parseurs (notamment quand les modules device et ebpf seront spécifiés), tests d’injection de fautes via simulateur de syscall, CI sur machines de référence (Raspberry Pi 4 ARM64 + Mac Intel x86_64 à chaque commit, cf. ADR-014).

Choix laissés ouverts pour la suite

  • Version kernel Linux minimale requise : 6.12 (LTS) — décidée, pas ouverte. io_uring mature, Landlock présent, cgroups v2 standard, eBPF complet. Aucun fallback vers les kernels antérieurs.
  • Spécification des familles device (uevent/sysfs/evdev) et ebpf (perf_event_open inclus) restant à produire.
  • Choix entre libbpf-rs et aya pour le binding eBPF haut niveau quand la famille ebpf sera spécifiée (cf. ADR-024 workflow dépendances).
  • Organisation précise des feature flags pour dégradation gracieuse selon fonctionnalités kernel disponibles.

Notes pour suite

Virtualisation (KVM, libvirt, qemu) : pas tier-1, mais à traiter ultérieurement en ADR futur dédié quand le projet aura mûri (utilisation de containers/VMs sur Air OS pour développement, isolation, compatibilité).


Section 3 — Couche 1 : Primitives système

Rôle et périmètre

La couche 1 fournit les briques fondamentales que toutes les couches supérieures consomment pour faire de la programmation système. Là où la couche 0 expose des syscalls et des primitives kernel, la couche 1 expose des concepts applicatifs : fichiers ouvrables, sockets connectables, processus contrôlables, données cryptographiques, devices interrogeables. C’est l’équivalent de la bibliothèque libSystem sur d’autres systèmes Unix : la couche que tout le monde utilise sans même y penser.

La couche 1 est écrite en Rust pur et exposée également en ABI C pour les consommateurs polyglottes. Elle vise une couverture de tests de 100 % (cf. Principe d’ingénierie 1) et constitue avec la couche 0 le socle dont la fiabilité conditionne tout le reste.

Son périmètre couvre :

  • Gestion de processus de haut niveau : lancement, supervision, communication parent/enfant, signaux applicatifs.
  • Filesystem : chemins, opérations atomiques, watchers, gestion temporaire, opérations sécurisées.
  • Sockets et networking bas niveau (air-socket) : connexions TCP/UDP/Unix, écoute, manipulation d’adresses. La stack réseau de haut niveau air-network au sens du futur ADR « framework networking couche 2 » vient en couche 2.
  • Primitives cryptographiques : hash, signatures, symétrique, asymétrique, dérivation de clés.
  • Devices via libudev : énumération, propriétés, surveillance.
  • Time et timers : horloges, deadlines, intervalles.
  • Threading et synchronisation : threads, mutex, channels, atomics, primitives de coordination.
  • Allocation et structures mémoire : pools, arenas, allocateurs custom pour cas spécifiques.

Tout ce qui est sémantiquement applicatif (le runtime d’objets C-ABI, l’IPC AirCom, l’event loop unifié, les services système) est en couche 2 ou au-dessus.

Les crates de la couche 1

air-base-core/    — cœur sans i18n : erreurs, chaînes/chemins/octets, temps (ADR-054)
air-base-lib/     — fondations universelles sur air-base-core (Unicode/i18n via icu4x, services)
air-filesystem/   — opérations filesystem haut niveau (AirFile, watcher)
air-process/      — gestion processus applicative (fork/exec, privilege separation, spawn)
air-thread/       — threading et synchronisation (futex, mutex/cond/rwlock)
air-memory/       — pools, arenas, mappings partageables (MmapRegion)
air-alloc/        — allocateur global possédé (inspiré de dlmalloc, ADR-056)
air-socket/       — sockets et networking bas niveau (ex air-network)
air-device/       — énumération et surveillance devices via libudev
air-crypto/       — primitives cryptographiques (RustCrypto)
air-terminal/     — AirTerminalManager : terminaux Linux (termios, PTY, codec ANSI, ADR-060/061)
air-signal/       — face signal générique (signalfd, masques, ADR-064)
air-poll/         — multiplexeur d'événements générique (ppoll)
air-stdio/        — brique stdio/console bas niveau
air-env/          — environnement no_std (variables, args, octets)
air-account/      — comptes /etc/passwd·shadow·group, bindé par la libc (ADR-067)
air-handle/       — registre de handles fd-général + duplicateur (ADR-069)
air-config/       — compilateur de configuration + artefact Cap'n Proto (ADR-040)
air-libm/         — math flottante no_std (fondation libc/i18n, ADR-057)
air-runtime/      — runtime userland : AirRuntime, TCB, TLS, relocation (ADR-052)

Toutes exposent une API Rust idiomatique. Cette API est interne au projet Air : elle outille les toits (la libc libair_c et le PAL std) et n’est pas un contrat public pour les développeurs d’applications (cf. la doctrine de stabilité, Section 10) — elle évolue avec le kernel. Les toits ne consomment jamais la couche 0 en direct : ils passent par les Managers de domaine de la couche 1 (voir ci-dessous).

Managers de domaine (ADR-077)

Le rôle de la couche 1 est de transformer les « objets » fonctionnels du kernel en objets Rust, et de les faire manipuler par des Managers de domaine : des objets médiateurs, un par domaine — AirFileManager, AirTaskManager, AirNetworkManager, AirSystemManager, AirSignalManager, AirEnvironmentManager, AirMemoryManager, AirTerminalManager. C’est la surface que consomment les toits (la libc libair_c et le PAL std) : jamais la couche 0 en direct, jamais des fonctions libres éparses. Les Managers évoluent avec les features du kernel — ils adoptent les variantes modernes et déprécient, voire abandonnent, le legacy. Air se réserve le droit de faire évoluer leur comportement pour coller à la fois aux idiomes Rust et à ceux du kernel Linux : ils ne sont pas un contrat public pour les développeurs d’applications (cf. Section 10).

air-base-lib : fondations universelles

La crate la plus consommée du système. Contient :

Types d’erreur et result. AirError est le type d’erreur générique, conçu pour absorber facilement les erreurs des couches plus basses (AirSysError) et exposer une chaîne d’erreurs traçable. Cohérent avec Principe d’ingénierie 4.

Strings et paths. AirString Unicode-aware (enrobe String Rust avec opérations Unicode normalisées via icu4x, cf. ADR-016). Méthodes : normalisation NFC/NFD, casing locale-aware, segmentation par grapheme, comparaison locale-aware. AirPath représente un chemin de fichier, distinct d’AirString parce qu’un chemin Unix peut contenir des octets non-UTF8 (cohérent avec Principe d’ingénierie 3). Conversion explicite entre les deux.

Time. AirInstant (point dans le temps monotone), AirDuration (durée), AirDateTime (date et heure dans un calendrier donné, via icu4x). Distinction nette entre temps physique monotone et temps calendaire.

Logging structuré. AirLog est l’API de logging unifiée. Pousse vers journald via sd-journal (cohérent ADR-005). Format structuré (clés/valeurs typées), niveaux standard, contexte attachable. Intégration avec tracing standard Rust.

Identifiants. AirUuid (UUID v7 préféré), AirId128 (générique, utilisé pour machine-id via sd-id128), AirMonotonicId (compteur monotone).

Configuration. AirConfig API standard pour lire la config d’un composant. Format TOML par défaut. Recherche selon conventions XDG et Air. Validation par schéma (typage Rust + serde).

Pas de macros dérivées ici. L’objectif d’air-base-lib est d’être consommable depuis le C aussi simplement que possible. Les macros idiomatiques Rust (comme #[derive(AirObject)]) sont en couche 2.

air-filesystem : opérations filesystem

Au-delà des primitives couche 0 :

  • Opérations atomiques : AirFs::write_atomic(path, data) (tmpfile + fsync + rename).
  • Watchers : AirFsWatcher enrobant inotify, avec callback, filtrage, debouncing.
  • Répertoires temporaires : AirTempDir avec cleanup automatique au drop.
  • Chemins canoniques : résolution symlinks, normalisation, vérification de confinement.
  • Copie efficace : AirFs::copy via copy_file_range zero-copy sur FS qui le supportent.
  • Recherche : globs, regex, bornée pour éviter explosions.

air-process : gestion de processus applicative

  • AirProcess typé : arguments, environnement, redirections, wait, signaux.
  • Pipes typés : AirPipe wraps pipe2() avec flags.
  • Pattern privilege separation : AirProc::drop_privileges() helper atomique (retire capabilities, change UID/GID, applique seccomp en une opération). Utilisé partout dans les services Air, cohérent avec Principe d’ingénierie 10.
  • Pas de supervision longue durée : confiée à systemd via air-launchd couche 5.

air-socket : sockets et networking bas niveau

Renommage validé : la crate sockets bas niveau de la couche 1 s’appelle air-socket, pour éviter la collision avec air-network (couche 2 — ADR à produire — framework networking).

  • Sockets typés : AirTcpSocket, AirUdpSocket, AirUnixSocket, AirNetlinkSocket.
  • Adresses : AirIpAddress (v4 ou v6), AirSocketAddress, AirUnixAddress.
  • Helpers DNS : résolution synchrone et asynchrone via resolver système. Pas de DNS Air spécifique (cohérent futur ADR « framework networking couche 2 »).
  • Pas de framework de connexion sémantique : les notions AirConnection, AirHttpRequest, etc. prévues par le futur ADR « framework networking couche 2 » sont en couche 2.

air-crypto : primitives cryptographiques

  • Choix de socle : RustCrypto. Hash (SHA-2, SHA-3, BLAKE3), MAC (HMAC), symétrique (AES, ChaCha20), asymétrique (Ed25519, X25519, RSA), KDF (HKDF, Argon2), AEAD (AES-GCM, ChaCha20-Poly1305). Maintenu, audité, performant.
  • TLS via rustls : pas d’OpenSSL. Air n’a pas besoin de la dette historique d’OpenSSL.
  • Accélération matérielle : AES-NI sur Intel x86_64, Crypto Extensions ARM v8 récents (automatique via RustCrypto).
  • Random haute qualité : AirRandom enrobant getrandom (toujours via kernel, jamais PRNG userspace).
  • Stockage de secrets pas ici : keychain en couche 5 (futur ADR).
  • Capabilities pour usage avancé : primitives disponibles à tous, mais utilisation pour signer bundles .airapp réservée à air-trust (couche 5).

air-device : énumération et surveillance via libudev

  • Choix de socle : libudev (cohérent ADR-005).
  • Énumération : AirDevice::enumerate(filter) avec filtres (subsystem, sysname, propriétés).
  • Propriétés : exposition des propriétés device, typage pour propriétés courantes (vendor/product ID, MAC).
  • Surveillance hotplug : AirDeviceMonitor consommant uevents via libudev, stream typé d’événements.
  • Pas de logique politique : air-device énumère et notifie. Les décisions sont en couche 5.

air-thread : threading et synchronisation

  • AirThread : lancement avec config (nom, taille stack, affinité CPU).
  • Synchronisation : AirMutex, AirRwLock, AirSemaphore au-dessus de parking_lot.
  • Channels : AirChannel MPSC.
  • Atomics : ré-export des types stdlib pour cohérence.
  • Pas d’async runtime ici : le runtime async (air-event) est en couche 2 — natif io_uring, sans tokio (ADR-038).

air-memory : allocations spécialisées

  • Arenas : AirArena pour patterns alloue-massivement-libère-d’un-coup.
  • Object pools : AirObjectPool<T> pour types fréquemment alloués/libérés.
  • Slab allocators : pour structures à taille fixe.
  • Memory tracking : API pour mesurer consommation par composant (audits fin de phase, validation cibles modestes Principe 9).
  • Pas de GC : Rust gère via ownership.

air-runtime : runtime userland (AirRuntime)

Le runtime userland d’Air (ADR-049, placement corrigé par ADR-052) est un objet de couche 1, pas un étage à part : un programme a besoin, au démarrage, d’un runtime (crt0) qui n’est pas que de la gestion de thread. air-runtime héberge les objets Rust sûrs qui le composent — tous couche 1, donc consommant la couche 0 comme n’importe quel autre objet couche 1 :

  • ThreadControlBlock : bloc de contrôle de thread au registre TLS (errno thread-local, self-pointer, canari de pile issu d’AT_RANDOM, DTV, tid, mot futex de join).
  • ThreadLocalStorage : mise en place TLS ELF (Variant II x86_64 / Variant I aarch64), lecture du gabarit PT_TLS, programmation du registre TLS (set_fs couche 0 / msr tpidr_el0).
  • Outils de relocation : la cible *-linux-air étant PIE/static-PIE (ADR-050), le runtime applique lui-même ses relocations R_*_RELATIVE (parcours auxv/phdrs/.dynamic) — outillage ELF en Rust pur, sous contrainte de bootstrap.
  • AirRuntime : compose les trois ci-dessus avec le décodage de la pile initiale (argc/argv/envp/auxv), l’exposition d’environ/args, le spawn std-free et la terminaison de processus — en s’appuyant sur air-thread (spawn/futex), air-memory (mmap), air-process (exit).

Invariant (ADR-052) : AirRuntime ne consomme que la couche 1 ; il ne dépend jamais de air-sys-syscall (la surface kernel ; air-sys-types — types transverses — reste autorisé partout, cf. ADR-052 D6). La libc Air et le std de *-linux-air reposent sur AirRuntime. Seul le point d’entrée ELF _start (trampoline asm minimal, spécifique à la cible) vit hors-arbre dans rt/ (ADR-050, nightly + build-std) et appelle AirRuntime.

Pas d’ABI C publique en couche 1 — les toits libair_c et le PAL

L’ancienne ABI C de la couche 1 (libair-base.so) est retirée : la couche 1 n’expose pas de surface publique stable — elle est interne, non contractuelle, et évolue avec le kernel (cf. la doctrine de stabilité, Section 10). Elle alimente en revanche, via ses Managers, les deux toits construits au-dessus d’elle :

  • libair_c — la libc C d’Air (crates air-libc-*), conforme au kernel Linux, pas à POSIX (cf. ADR-046/047). C’est ce que lient le C et le std Rust de la cible *-linux-air (zéro glibc). Elle donne au développeur C une base attendue et sûre ; pour faciliter le portage d’outils Unix elle expose des symboles approchant POSIX, mais dont le comportement suit Linux — un symbole hérité néfaste (p. ex. pthread_cancel) peut être fourni déprécié / inerte pour permettre le linkage, sans jamais recevoir de vraie implémentation.
  • Le PAL (std::sys de la cible *-linux-air) — le backend qui fait tourner le std Rust natif sur Air.

La surface publique stable d’Air pour les développeurs vit en couche 2 : la lib Rust libair (Rust pur) et les ABI C du modèle d’objet (libair-object.so, etc.), zone air-stable, garantie 10 ans (cf. ADR-012, Section 4).

Tests : coverage 100 % obligatoire

Stratégies similaires couche 0 + tests d’ABI C de la libc libair_c (compilation C + appels), tests d’intégration multi-processus, property-based testing pour types complexes (AirString Unicode, AirPath normalisation, AirDateTime calendriers), fuzzing pour parseurs (TOML, paths, propriétés udev).

Choix laissés ouverts pour la spec de composant

  • Primitives de synchronisation bloquantes : std::sync/parking_lot (cf. spec air-thread). L’async (couche 2) repose sur le runtime io_uring natif air-event, pas sur tokio (ADR-038).
  • Politique précise de logging : format des messages structurés, conventions de nommage, niveaux par défaut. À standardiser début phase 0.
  • Bindings polyglottes auto-générés vs écrits manuellement : cbindgen, uniffi (Mozilla). À évaluer phase 1.
  • Politique d’allocation par défaut : system allocator, jemalloc, mimalloc. Probablement system allocator sur Air OS.

Section 4 — Couche 2 : Modèle d’objet, AirCom, services fondamentaux

Rôle et périmètre

La couche 2 est le cœur conceptuel d’Air. Elle introduit les abstractions qui rendent le système cohérent verticalement (du kernel aux applications) et horizontalement (entre tous les composants). Trois piliers la composent :

  • Le runtime d’objets C-ABI (air-object) qui donne à toute classe d’Air une identité runtime, des propriétés observables, et la bindabilité polyglotte. Décidé par ADR-002.
  • AirCom (air-com) qui fournit l’IPC capability-based, schema-first, performant, qui remplace D-Bus pour les communications internes. Décidé par ADR-001.
  • Les services fondamentaux que toute couche supérieure consomme : event loop unifié, intégration systemd, collections Unicode-aware via icu4x, notification, observation de propriétés, registre de services.

Cette couche est écrite en Rust et exposée intégralement en ABI C : c’est précisément ici que se matérialise la propriété “Air est polyglotte dès le départ”. Une application en Swift, Python ou Ruby consomme libair-object.so et libair-com.so directement, sans glue spécifique par classe.

La couche 2 ne contient pas de logique applicative spécifique (pas de compositeur, pas de framework UI, pas de service système au sens fonctionnel). Elle contient les mécaniques qui permettent à ces composants existants d’être cohérents.

Partie A — Le runtime d’objets air-object

Le problème à résoudre

Rust, par design, n’a pas de runtime d’objets dynamique. C’est une force pour la performance et la sûreté, mais insuffisant pour quatre besoins :

  • Observation de propriétés : fondement du data binding, des frameworks réactifs (air-ui), de l’accessibilité (lecteur d’écran), du débogage (inspecteur).
  • Introspection runtime : pour les outils (debugger, inspecteur d’UI, lecteur d’écran) qui doivent interroger un objet sans connaître son type concret.
  • Bindings polyglottes sans glue par classe : pour qu’un script Python ou Swift consomme Air uniformément.
  • Comptage de références cross-langage : pour qu’un objet créé en Rust et référencé par Swift/C/Python vive correctement.

ADR-002 a tranché : Air a un runtime d’objets C-ABI pour le périmètre “CoreFoundation/AppKit” (collections, strings, URLs, services, vues, contrôleurs, propriétés observables). Tout le reste reste Rust pur.

Architecture du runtime d’objets

L’unité fondamentale est AirObject, structure C opaque :

AirObject (taille variable selon classe)
├── header (24 octets sur 64-bit)
│   ├── classe        — pointeur vers la AirClass
│   ├── refcount      — compteur de référence atomique
│   └── flags         — frozen, observed, traits accessibility, etc.
└── payload (taille variable, dépend de la classe)
    └── champs de la classe, dont propriétés observables

Toute AirClass est elle-même un AirObject (méta-classe). Une classe porte :

  • Sa table virtuelle : pointeurs vers les fonctions méthodes.
  • Ses métadonnées d’introspection : nom, parente, propriétés, méthodes.
  • Ses traits accessibility : role par défaut, attributs universels.

API publique C-ABI :

air_object_alloc(class)                 — allocation
air_object_retain(obj) / release(obj)   — refcount
air_object_class(obj) / is_kind_of      — type info
air_object_get_property / set_property  — propriétés par nom
air_object_observe / unobserve          — abonnement aux changements
air_object_send_message                 — invocation de méthode dynamique

AirValue est un type union typé : entier, flottant, booléen, string, bytes, autre AirObject, null. Équivalent NSObject/id macOS ou GValue GObject.

Définition des classes en Rust

Via macros :

#![allow(unused)]
fn main() {
#[air_class]
pub struct AirButton {
    #[observable]
    pub label: AirString,
    
    #[observable]
    pub enabled: bool,
    
    #[accessibility(role = "button")]
    _accessibility: AccessibilityMarker,
    
    on_click: Option<AirCallback<()>>,
}
}

Les macros génèrent : structure C opaque avec layout C-ABI, AirClass métadonnée enregistrée au runtime, implémentations standard (retain/release/getProperty/etc.), fonctions C exportées (ABI stable), annotations accessibility consommées par air.wm.accessibility (ADR-017).

AirHandle<T> : smart pointer Rust avec sémantique Arc interopérant avec refcount AirObject (Drop = release, Clone = retain).

Bindings polyglottes

Pattern A : binding générique (Python, Ruby, dynamiques) — consomme uniquement les fonctions universelles air_object_*. Aucun code spécifique par classe.

Pattern B : binding typé (Swift, statiques avancés) — génère wrappers typés à partir des métadonnées. Swift via @dynamicMemberLookup pour syntaxe quasi-native.

Pont avec le monde Rust pur

Toutes les structures Air ne sont pas des AirObject. Pour structures internes, parseurs, algorithmes : Rust pur (pas de surcoût refcount atomique, pas de table virtuelle). Frontière explicite, jamais implicite (Principe d’ingénierie 7).

Mémoire et threads

Refcount atomique : objet détenu par plusieurs threads. Mais contenu pas thread-safe par défaut. Politique explicite obligatoire :

  • Immutable : créée une fois, jamais modifiée (thread-safe trivialement, modèle AirString).
  • MainThreadOnly : thread principal uniquement (modèle AirView, vérifié en debug).
  • ThreadSafe : accès concurrent autorisé (implémentation protège invariants).

Classe sans politique déclarée ne compile pas.

Introspection et debugging

air_class_list_all() / air_class_properties / air_class_methods / air_class_parent
air_object_describe / air_object_children

Activée en production. Permet à air-screenreader (cf. ADR-017) de fonctionner sur n’importe quelle app sans plugin.

Crates concernées

air-object/         — runtime principal, ABI C
air-object-macros/  — macros procédurales
air-value/           — AirValue
air-collections/     — AirArray, AirDictionary, AirSet (observables)

(AirString — chaîne UTF-8 Unicode-aware sur icu4x — est un type de la couche 1 [air-base-lib], re-exporté/consommé ici, pas une crate de la couche 2.)

ABI C via libair-object.so, zone air-stable.

Partie B — AirCom air-com

Rappel des décisions structurantes (ADR-001)

Unix sockets pour control plane, memfd_create + mmap pour data plane bulk, io_uring pour async, modèle capability-based inspiré XPC/Cap’n Proto/FIDL, encodage schema-first avec Cap’n Proto, pas de broker central, registre léger pour découverte.

Couche 2 implémente AirCom dans deux crates : air-com (transport et runtime) et air-com-schema (compilateur de schémas).

Le modèle capability-based

Une capability AirCom est un handle opaque non-falsifiable. Techniquement : FD vers une connexion AirCom, plus métadonnées de type.

Distribution initiale. air-launchd (couche 5) crée les connexions selon les entitlements et passe les FDs via SCM_RIGHTS :

fn main() {
    let cx = AirContext::from_environment();
    let notifications = cx.capability::<NotificationService>()?;
    // obtenu uniquement si entitlement air.system.notifications déclaré
    notifications.send(Notification::new("Hello")).await?;
}

Pas d’autorité ambiante : un processus ne peut parler qu’aux services dont il a reçu une capability.

Délégation. Capability passable à un autre processus via AirCom lui-même (encodée comme SCM_RIGHTS). Composition : un service peut déléguer une sous-capability sans passer par air-launchd. Modèle seL4 / Mach ports macOS.

Révocation. Par fermeture FD côté serveur, invalide immédiatement côté client.

Schémas et typage

Schema-first via Cap’n Proto. Chaque service décrit par un schéma définissant méthodes, types, versioning :

@0x9f5b87a3c0b46d3e;

interface NotificationService @0 {
  send @0 (notification :Notification) -> (id :UInt64);
  cancel @1 (id :UInt64) -> ();
  
  struct Notification {
    title @0 :Text;
    body @1 :Text;
    # ...
  }
}

Compilateur air-com-compile génère : module Rust avec types et traits, header C correspondant, optionnellement bindings polyglottes.

Évolution des schémas (cohérent ADR-012)

Règles strictes Cap’n Proto : ajout champ OK (ignoré par ancien client), ajout méthode OK, suppression jamais, renumérotation jamais, changement de type jamais.

Versions d’interface dans les noms (NotificationServiceV2 coexistant avec V1). Registre AirCom présente les deux, l’app choisit.

Transport et performance

  • Control plane : Unix sockets SEQPACKET (ordering garanti, frontières préservées, FD passing).
  • Data plane bulk : memfd + mmap pour payloads volumineux (images, frames vidéo, buffers GPU). Zero-copy entre processus.
  • Async via io_uring : opérations AirCom async par défaut, batchées quand possible. Performance comparable XPC ou Cap’n Proto natif.
  • Back-pressure explicite : modèle reactive streams. Pas de mémoire qui gonfle silencieusement.

Registre de services : air-registry

Seul service système “fondamental” : fournit la découverte. Quand un service expose une interface, il s’enregistre auprès d’air-registry. Clients qui ne connaissent pas l’emplacement le demandent.

Pas un broker : ne relaie pas. Fournit juste la résolution. Une fois connexion établie, dialogue direct.

Capability initiale universelle. Tout processus Air reçoit au démarrage la capability vers air-registry. Seule capability implicite. Toutes les autres explicitement déclarées.

Crates concernées

air-com/                  — transport, runtime
air-com-schema/           — compilateur de schémas
air-com-codegen/          — génération bindings
air-registry/                 — service registre (lib + daemon)

ABI C via libair-com.so, zone air-stable.

Partie C — Services fondamentaux et orchestration

Event loop unifié : air-event

Runtime asynchrone natif sur io_uring (ADR-023, ADR-038) — sans tokio. Le moteur est le crate air-runtime (couche 1, consomme io_uring) ; air-event en est la façade C-ABI (couche 2). Cf. ADR-039. (tokio avait été envisagé au tout début, avant le support intégral d’io_uring ; abandonné depuis.)

Pourquoi air-event. Pour exposer un event loop async consommable depuis C/Swift/Python, il faut une API C-ABI propre bâtie directement sur io_uring. air-event est ce runtime et cette API.

Concepts exposés : AirEventLoop, AirFuture (polymorphique exposable en C), AirTimer, AirSignal (via signalfd couche 0), AirChannel (MPSC async).

Integration AirCom : toute opération AirCom retourne AirFuture. Coordination naturelle AirCom + timers + FDs.

Integration sd-event : peut consommer source sd-event quand composant Air doit s’intégrer systemd (watchdog, sd-notify).

Runtime maison sur io_uring : Air maîtrise entièrement son runtime async (aucune dépendance externe type tokio à encapsuler ou remplacer). Cf. ADR-038.

Intégration systemd : air-systemd

Encapsule intégration systemd (cohérent ADR-005).

  • sd-bus peer-to-peer : client sd-bus configurable pour parler à systemd directement via socket privé sans dbus-daemon. Mécanisme principal pour logind, systemd --user, systemd-resolved, etc.
  • sd-event integration : pont vers air-event.
  • sd-notify : API pour qu’un service Air notifie systemd (READY, RELOADING, STOPPING, WATCHDOG).
  • sd-journal : API logging structuré, consommée par AirLog couche 1.
  • sd-id128 : identifiants 128-bit, consommés par AirId128 couche 1.
  • Pont D-Bus optionnel : air-dbus-bridge chargeable à la demande. Pas requis.

Collections Unicode-aware

AirArray<T>, AirDictionary<K, V>, AirSet<T>, AirOrderedSet<T> — observables. Tous AirObject, donc C-ABI et bindables.

Pour usages internes performants : Vec, HashMap stdlib Rust. Conversion explicite vers collections C-ABI.

Observation et notification

  • AirObservable<T> : wrapper rendant un champ observable. Get/set/observe.
  • AirNotificationCenter : bus de notifications nommées intra-processus. Modèle NSNotificationCenter macOS. Distinct de AirCom (qui est inter-processus).

Préférences utilisateur : air-prefs (ADR à produire — préférences utilisateur)

Détaillé dans le futur ADR — air-prefs. Service AirCom fondamental en couche 2 (par sa nature de service consommé par toutes les couches supérieures). Trois domaines hiérarchiques (système / utilisateur global / app) avec cascade de fallback. Format TOML. Schémas typés déclaratifs (modèle GSettings simplifié). Capabilities air.preferences.read/write.{own-app,global,other-app}. Observation async via AirCom. Versioning explicite des schémas avec migrations déclaratives. Compatibilité freedesktop via xdg-desktop-portal-air (Settings portal).

Architecture d’ensemble de la couche 2

                  ┌──────────────────────────────────┐
                  │  Couches 3, 4, 5 consomment      │
                  │  les APIs publiques de la 2      │
                  └─────────────┬────────────────────┘
                                │
   ┌────────────────────────────┼─────────────────────────────┐
   │                            │                             │
   ▼                            ▼                             ▼
┌────────┐               ┌───────────┐                ┌──────────────┐
│Runtime │◄─────────────►│  AirCom  │◄──────────────►│ Event loop   │
│d'objets│   métadonnées │   IPC     │   intégration  │ air-event    │
│ C-ABI  │   classes     │           │   async        │              │
└────┬───┘               └─────┬─────┘                └──────┬───────┘
     │                         │                             │
     │     ┌───────────┐       │      ┌──────────────┐       │
     ├────►│Collections│       ├─────►│  air-systemd │       │
     │     │Unicode    │       │      │  sd-*        │◄──────┤
     │     │AirString  │       │      └──────┬───────┘       │
     │     └───────────┘       │             │               │
     │                         │             ▼               │
     │                  ┌──────▼──┐   ┌──────────────┐       │
     │                  │air-     │   │   systemd    │       │
     │                  │registry │   │   (couche    │       │
     │                  └─────────┘   │   externe)   │       │
     │                                └──────────────┘       │
     ▼                                                       ▼
   ┌─────────────────────────────────────────────────────────┐
   │  Couches 0 et 1 (air-sys-*, air-base-lib, air-socket...)│
   └─────────────────────────────────────────────────────────┘

Crates de la couche 2 — récapitulatif

air-object/             — runtime d'objets C-ABI
air-object-macros/      — macros procédurales
air-value/               — AirValue
air-collections/         — AirArray, AirDictionary, AirSet

air-com/             — transport IPC
air-com-schema/      — schémas Cap'n Proto
air-com-codegen/     — génération bindings
air-registry/            — service registre

air-event/               — event loop unifié
air-systemd/             — intégration systemd
air-dbus-bridge/         — pont D-Bus optionnel
air-prefs/               — préférences (spec ultérieure)
air-notification/        — AirNotificationCenter intra-processus

ABI C via libair-object.so, libair-com.so, libair-event.so, zone air-stable.

Tests : coverage 100 % obligatoire

Stratégies couches 0-1 + tests ABI C runtime depuis C/Swift/Python, tests AirCom cross-process (lancer service et client en processus séparés), tests d’évolution de schéma (version N parle à N+1), fuzzing transport AirCom (parsing messages externes), tests multi-thread (refcount atomique, race conditions).

Choix laissés ouverts pour la spec de composant

  • Compilateur Cap’n Proto : capnp crate Rust officiel au départ, règle des 80 % à vérifier.
  • Stratégie de génération de bindings polyglottes : cbindgen headers C, uniffi Swift/Kotlin/Python. À évaluer phase 1.
  • Politique de garbage collection : refcount strict au départ, cycles évités par discipline (modèle ARC macOS), GC cyclique reporté.
  • Format final des macros #[air_class] : exemple indicatif, détail à l’implémentation.
  • Politique précise de MainThreadOnly enforcement : debug uniquement ou release avec overhead minimal. À mesurer.

Section 5 — Couche 3 : Composition, rendu, audio, input

Rôle et périmètre

La couche 3 est l’interface physique d’Air avec le matériel d’affichage et de son. Elle prend en entrée des descriptions logiques et produit des artefacts matériels.

Son périmètre couvre :

  • Composition d’écran : deux compositeurs jumeaux partageant leur backend. air-wm Wayland graphique (ADR-003), air-console mode texte bypass VT (ADR-007).
  • Rendu 2D : GPU-first via Vello pour graphique, grille de cellules pour console. Texte bidirectionnel via Cosmic Text + HarfBuzz.
  • Audio : intégration PipeWire.
  • Input : evdev partagé, gestion seats via logind, traduction événements bruts en logiques.
  • Gestion matériel d’affichage : DRM/KMS, hotplug, multi-écran, propriétés étendues (HDR, espace colorimétrique, refresh rates, DPI).

Tout ce qui est décision applicative sur quoi afficher est en couche 4. La couche 3 n’est présente que dans air-desktop (cf. ADR-006), à l’exception d’air-console qui peut être inclus dans air-base.

Vue d’ensemble

┌─────────────────────────────────────────────────────────────────┐
│  Couches 4 et 5 consomment la couche 3                          │
└────────────┬────────────────────────────────┬───────────────────┘
             │                                │
             ▼                                ▼
   ┌──────────────────┐              ┌──────────────────┐
   │    air-wm        │              │   air-console    │
   │  compositeur     │              │  compositeur     │
   │  Wayland         │              │  texte           │
   └────────┬─────────┘              └────────┬─────────┘
            │                                 │
            └──────────┬──────────────────────┘
                       │
                       ▼
            ┌─────────────────────┐         ┌──────────────────┐
            │   air-display       │         │   air-audio      │
            │   (backend partagé) │         │  PipeWire bridge │
            │  DRM/KMS + evdev    │         └────────┬─────────┘
            │  + seats + glyph    │                  ▼
            └──────────┬──────────┘         ┌──────────────────┐
                       │                    │   PipeWire       │
                       │                    └──────────────────┘
                       ▼
            ┌─────────────────────┐         ┌──────────────────┐
            │  Couches 0, 1, 2    │         │   air-render     │
            │  + air-com      │         │   (Vello 2D)     │
            └─────────────────────┘         └──────────────────┘

Partie A — Backend partagé air-display

ADR-007 a posé la symétrie : air-wm et air-console bypassent les abstractions historiques pour parler directement au matériel. Conséquence : ils partagent une couche d’abstraction matérielle commune, air-display.

air-display est une bibliothèque, pas un compositeur à lui seul. Elle gère le matériel ; les compositeurs gèrent la politique.

DRM/KMS backend. Bindings haut niveau sur l’API Direct Rendering Manager. Types : AirDrmDevice (GPU disponible), AirOutput (sortie physique connectée), AirCrtc (contrôleur scan-out), AirFramebuffer (buffer GPU prêt à afficher), AirPlane (plan de composition matériel). Programmation atomique de DRM exposée (multi-écran cohérent). Choix de socle : crate drm Rust à évaluer phase 0.

evdev backend. Bindings haut niveau sur input_event. Types : AirInputDevice (clavier, souris, touchpad, tactile, gamepad — énuméré via libudev couche 1), AirInputEvent (événement brut typé), AirSeat (groupe de devices via logind). Acquisition EVIOCGRAB exposée pour mode exclusif.

Gestion des seats via logind. air-display consomme logind via air-systemd (couche 2). Permet à air-wm ou air-console de tourner sans être root, en utilisant leur place dans le seat.

Glyph rasterizer. Rendu de glyphes texte centralisé. Stack : cosmic-text pour layout (shaping, line breaking, bidi), HarfBuzz pour shaping spécifique (latin, arabe avec ligatures, devanagari avec diacritiques), Swash ou équivalent pour rasterisation, cache de glyphes partagé indexé par (police, taille, glyph_id, sub-pixel). Polices Noto par défaut (cohérent ADR-016).

Partie B — air-wm : compositeur Wayland

Cf. ADR-003 (compositeur Wayland) et ADR à produire — protocole privé AirCom (services privés).

Sous-systèmes :

  • Cœur Wayland : implémentation protocole Wayland + extensions standards (xdg-shell, xdg-decoration, linux-dmabuf, text-input-v3). Smithay comme base candidate.
  • Backend air-display : tout accès matériel.
  • Scene graph : représentation hiérarchique avec damage tracking. Performance critique sur matériel modeste.
  • Renderer : composition GPU via Vello, composition matérielle directe via DRM planes pour cas simples.
  • Window manager logic : placement, workspaces (par écran selon le futur ADR « protocole privé AirCom »), modes multi-écran, raccourcis système, focus.
  • Services AirCom privés : les sept catégories prévues par le futur ADR « protocole privé AirCom » (accessibility, animation, drag-drop, clipboard, notifications, devtools, displays) + air.wm.session (négociation initiale), air.wm.screencapture (ADR à produire — air-screencapture/air-screenrecord), air.system.appearance (ADR à produire — air.system.appearance / theming).

Shell intégré ou externe ? Décision validée : air-shell est un processus séparé en couche 5, pour respecter la discipline d’isolation et permettre modularité. Réévaluation possible si overhead trop important sur petites configurations.

Protocole Wayland natif + extension AirCom. Pour apps clientes Wayland standards : qualité de base élevée. Pour apps Air natives : services AirCom privés en plus. Pas de protocole Wayland privé inventé (cohérent avec le futur ADR « protocole privé AirCom »).

Multi-écran professionnel (ADR à produire — protocole privé AirCom (catégorie displays)). Modes miroir/étendu/indépendants exposés sémantiquement, fullscreen ciblé par écran soigné, workspaces par écran configurables, hotplug à chaud sans casser session, propriétés étendues (HDR, espace colorimétrique, refresh rates, DPI réel) via air.wm.displays.

Performance. Cible 60 fps stable sur Raspberry Pi 4, jusqu’à 120/144 Hz sur matériel plus puissant. Latence input-to-photon < 30 ms. Stratégies : composition matérielle (DRM planes), damage tracking strict, VRR quand supporté, adaptation au mode énergie (cf. ADR à produire — air-power).

Partie C — air-console : compositeur de mode texte

Cf. ADR-007 et ADR-008.

Partage massivement de code avec air-wm via air-display. Différences sur le modèle de rendu et le modèle d’événement.

Modèle de rendu : grille de cellules. Chaque cellule porte : codepoints Unicode (graphèmes complexes — émojis, ligatures arabes, diacritiques), attributs de style (couleur fg/bg, gras, italique, souligné, etc.), largeur double pour CJK. Buffer 2D rastérisé via glyph rasterizer partagé. Rendu final = framebuffer pixel via DRM/KMS. La grille est une abstraction logicielle au-dessus d’un rendu pixel, pas une dépendance hérité.

Modèle d’événement. Événements typés aux apps connectées (vs flux ANSI à un terminal classique) : clavier complets (press/release/repeat, modifiers exacts, layout-aware), souris haute précision, multi-touch, redimensionnement grille, focus. Via protocole AirCom privé air-tui (mode 1 d’ADR-008, plein bypass).

Gestion des espaces de travail. Affichage simultané possible : tiling, full-screen avec switching, splits. À affiner en spec.

Bascule TTY classique ↔ air-console. Plusieurs TTYs cohabitent. Ctrl+Alt+F1..F12 historique. logind gère qui a accès au seat.

Cohabitation apps Unix classiques. Bash/vim/htop tournent dans un terminal Air qui est lui-même app air-tui. Le terminal Air émule un PTY pour les programmes legacy. Apps air-tui natives tournent directement sans émulateur.

Partie D — Rendu : air-render

Choix de socle : Vello (validé).

Référence moderne pour rendu 2D GPU-first en Rust : approche compute-shader, qualité graphique excellente, performances sur matériel modeste, alignement avec direction Rust d’Air. Cohérent avec ADR-009 qui cite l’écosystème Linebender.

API. Scene graph immédiate : AirScene, AirPath (formes vectorielles), AirBrush (remplissages), AirTextRun (texte). air-ui compile ses vues déclaratives vers AirScene que air-render rasterise.

Backend GPU. Vello compile compute shaders Vulkan via wgpu. Sur machines de référence : Vulkan disponible Mac Intel, sur Raspberry Pi 4 (driver v3d, Vulkan partiel — à valider en conditions réelles).

Cache de glyphes partagé avec compositeur via air-display.

Partie E — Audio : air-audio

Intégration PipeWire (cohérent ADR-005). Bibliothèque, pas service.

PipeWire = serveur audio Linux moderne, remplace PulseAudio et JACK. Mature, performant, supporte audio bas-latence pour production musicale comme audio desktop.

API exposée aux apps Air :

  • AirAudioStream : flux audio lecture/capture. Format, sample rate, latence cible.
  • AirAudioDevice : device physique ou virtuel via PipeWire. Énumération, propriétés.
  • AirAudioPolicy : abstraction “à quoi sert ce flux” — musique, voix d’app, notification, communication temps réel.

Audio Bluetooth délégué à PipeWire (gère A2DP, HFP/HSP). air-bluetoothd configure profils, PipeWire route.

Latence et qualité. PipeWire permet sub-10ms en mode pro. Cible standard Air : 20-30 ms. Mode pro audio disponible pour apps spécialisées.

Couche transverse : événements et compositeur de session

air-seat : bibliothèque commune pour air-wm et air-console. Maintient connexion logind, gère acquisition/libération du seat lors des bascules TTY, distribue FDs des devices aux compositeurs actifs.

Coordination énergétique. air-power (couche 5, ADR à produire — air-power) interagit avec couche 3 pour : refresh rate selon mode (60 Hz normal, 30 Hz low_power, max performance), qualité de rendu réduite en low_power (animations off, blur réduit), VRR quand bénéfique, throttling scan-out sur inactivité. Via AirCom (air-wm expose capabilities consommées par air-power).

Crates de la couche 3 — récapitulatif

air-display/            — backend matériel partagé (DRM, evdev, seats, glyph)
air-seat/               — gestion seats via logind

air-wm/                 — compositeur Wayland
air-wm-protocols/       — implémentations protocoles Wayland étendus

air-console/            — compositeur mode texte

air-render/             — bibliothèque rendu 2D (Vello-based)

air-audio/              — intégration PipeWire

Services AirCom exposés par air-wm et air-console font partie de leurs binaires respectifs.

Tests

Plus difficile à tester exhaustivement que couches 0-2. Stratégies adaptées :

  • Tests unitaires sur logique pure (damage tracking, layout multi-écran, transformations).
  • Tests d’intégration avec headless backend (vkms kernel module ou équivalent pour DRM virtuel).
  • Tests sur matériel de référence ADR-014 : screenshots comparés, FPS, latence, multi-écran, hotplug.
  • Tests d’interopérabilité Wayland : weston-clients, Smithay examples.
  • Fuzzing parseur protocole Wayland.

Coverage cible : >90% mais pas 100% obligatoire (chemins matériel-dépendants).

Choix laissés ouverts pour la spec de composant

  • Smithay comme base candidate à confirmer en phase 3 selon Principe 6 (règle des 80 %).
  • drm-rs vs wrapping manuel pour bindings DRM (phase 0).
  • Position d’air-shell (couche 5 séparé validé, réévaluation possible si overhead).
  • Cosmic-text vs alternatives pour layout texte (phase 0).
  • Stratégie fallback Vulkan sur GPU sans support complet (Raspberry Pi 4, phase 3 en conditions réelles).
  • API précise de air-render (scene graph immédiat directionnel, détail final à l’implémentation).
  • Politique précise cohabitation air-console ↔ TTY kernel (acquisition/libération devices).

Section 6 — Couche 4 : Frameworks applicatifs air-ui et air-tui

Rôle et périmètre

La couche 4 est l’interface qu’Air offre aux développeurs d’applications. Elle fournit les outils pour construire des UIs déclaratives, qu’elles soient graphiques (consommant air-wm en couche 3) ou textuelles (consommant air-console ou un terminal en couche 3).

Périmètre :

  • Modèle de composition de vues déclaratif, partagé entre les deux modes.
  • Substrat impératif air-view-core vivant dans le runtime d’objets C-ABI (couche 2).
  • Framework graphique air-ui avec renderer Vello.
  • Framework console air-tui avec renderer cellules et trois backends de transport (ADR-008).
  • Widgets fondamentaux : labels, boutons, listes, tables, formulaires, layouts.
  • Vues composites de haut niveau : panneaux de paramètres, calendriers, alertes, lecteurs média.
  • Animation déclarative first-class (ADR-009).
  • Accessibilité par construction via modèle d’objet (ADR-017).
  • API intermédiaire air-ui-controller pour mélanger déclaratif et impératif.

Vue d’ensemble

┌─────────────────────────────────────────────────────────────────┐
│  Applications .airapp / .airservice (consommateurs)             │
└────────┬──────────────────────────────────────┬─────────────────┘
         │                                      │
         ▼                                      ▼
   ┌──────────────────┐                  ┌──────────────────┐
   │     air-ui       │                  │    air-tui       │
   │  graphique       │                  │  texte           │
   └────────┬─────────┘                  └────────┬─────────┘
            │                                     │
            └─────────────────┬───────────────────┘
                              │
                              ▼
                   ┌──────────────────────────┐
                   │   air-ui-core            │
                   │   modèle déclaratif      │
                   │   State / Observable /   │
                   │   Environment            │
                   └────────────┬─────────────┘
                                │
                                ▼
                   ┌──────────────────────────┐
                   │   air-view-core          │
                   │   substrat impératif     │
                   │   (vit dans air-object  │
                   │    C-ABI, couche 2)      │
                   └────────────┬─────────────┘
                                │
            ┌───────────────────┴──────────────────┐
            ▼                                      ▼
   ┌──────────────────┐                  ┌──────────────────┐
   │ air-ui-render-   │                  │ air-ui-render-   │
   │ gpu (Vello)      │                  │ tui (cellules)   │
   └────────┬─────────┘                  └────────┬─────────┘
            │                                     │
            ▼                                     ▼
   ┌──────────────────┐                  ┌──────────────────┐
   │     air-wm       │                  │   air-console    │
   │   (couche 3)     │                  │   (couche 3)     │
   └──────────────────┘                  └──────────────────┘

Partie A — air-view-core : substrat impératif

ADR-009 a posé : modèle déclaratif au-dessus, substrat impératif en-dessous vivant dans runtime C-ABI couche 2. Permet : introspection runtime universelle (lecteur d’écran), consommation polyglotte, interopérabilité avec outils dev (air-inspector), mélange déclaratif/impératif via air-ui-controller.

Développeur d’app standard ne touche jamais air-view-core directement.

Classes fondamentales (toutes AirObject héritant propriétés accessibility universelles ADR-017) :

  • AirView : classe racine. Géométrie, visibilité, état, parent, enfants. Propriétés observables (frame, visible, enabled, children, accessibility_*).
  • AirControl : sous-classe pour éléments interactifs (boutons, champs, sliders). Gestion focus, événements d’activation.
  • AirContainer : sous-classe pour conteneurs purs (layouts). Logique de placement.
  • AirWindow : fenêtre top-level. Wayland sous air-ui, tile/session sous air-tui.
  • AirImage, AirText, AirCanvas : vues de contenu standard.

Hiérarchie de coordonnées : logiques (indépendantes du DPI), scale factor géré au rendu. Leading/trailing plutôt que left/right pour locales RTL (cohérent ADR-016, ADR-009).

Cycle de vie observable : created, attached, appeared, disappeared, detached, deallocated.

Partie B — air-ui-core : modèle déclaratif partagé

ADR-009 : air-ui et air-tui partagent le modèle de composition mais diffèrent par le renderer.

Les view descriptions : descriptions immutables produites à chaque cycle. Le framework compare avec la précédente et applique les diffs au substrat impératif.

#![allow(unused)]
fn main() {
fn counter_view(cx: &mut Cx) -> impl View {
    let count = cx.state(|| 0);
    
    column((
        text(format!("Count: {}", count.get()))
            .font_size(24)
            .accessibility_label("Compteur"),
        
        button("Increment")
            .on_press(move |_| count.set(count.get() + 1))
            .accessibility_hint("Incrémente le compteur de 1"),
    ))
    .padding(16)
    .spacing(8)
}
}

Les trois mécanismes de state (ADR-009) :

  • State<T> : état local par vue. Identifié par position d’appel sur Cx. Accédé via cx.state(|| initial).
  • Observable<T> : objets observables partagés vivant dans runtime C-ABI couche 2. Abonnement explicite cx.observe(&obj, callback).
  • Environment<T> : injection par contexte d’arbre. Typage par TypeId. Modèle SwiftUI environment.

Tous les accès explicites (Principe d’ingénierie 7). Pas de magie de traçage automatique.

Diff incrémental. Algorithme inspiré React reconciliation, adapté Rust et matériel modeste. Clés (view.key("item-3")) pour stabiliser identité dans listes dynamiques.

Composition par fonction, pas par classe. Modèle SwiftUI / Xilem / Iced / React function components transposé Rust avec passage explicite du Cx.

Macro view! optionnelle pour cas où syntaxe Rust devient verbeuse :

#![allow(unused)]
fn main() {
view! {
    column padding=16 spacing=8 {
        text("Count: {count}") font_size=24
        if count > 10 {
            text("Big number!") color=red
        }
        for item in items {
            list_row(item)
        }
    }
}
}

Strictement du sucre syntaxique. Mélange possible des deux styles.

Partie C — air-ui : framework graphique

air-ui-render-gpu : renderer prenant l’arbre de view descriptions, produisant frames GPU via Vello (via air-render couche 3). Gère rasterisation widgets, animation (interpolation entre frames, intégration vsync), compositing final (fenêtre Wayland reçoit buffer GPU).

Communication avec air-wm : Wayland standard pour opérations de base + AirCom privé pour enrichissements (ADR à produire — protocole privé AirCom). Sous compositeur tiers : Wayland fonctionne, AirCom privé indisponible, dégradation selon air.wm.session.

Widgets atomiques (air-ui-widgets)

Texte et contenu :

  • text(content) : texte avec style.
  • image(source) : image bitmap ou vectorielle (atomique, bas niveau).
  • icon(name) : icône système symbolique.

Contrôles :

  • button(label) : bouton avec action.
  • text_field(binding) : saisie single-line.
  • text_area(binding) : saisie multi-ligne.
  • toggle(binding) : switch on/off.
  • slider(binding, range) : curseur.
  • picker(binding, options) : sélection non-éditable.
  • combo_box(binding, options, allow_custom) : sélection éditable.
  • radio_button(binding, value) : sélection mutuellement exclusive.
  • color_picker(binding) : sélecteur couleur (palette du futur ADR « theming » + picker visuel).
  • progress(value) : variantes progress_linear, progress_circular, progress_indeterminate.
  • scroll_bar(orientation, binding) : barre standalone (intégrée automatiquement à scroll_view par défaut).
  • date_picker(binding) : sélection date inline locale-aware.

Conteneurs et layouts :

  • column(children) : empilement vertical.
  • row(children) : empilement horizontal.
  • grid(children) : grille 2D.
  • stack(children) : superposition z-order.
  • scroll_view(content) : zone scrollable (scroll bars automatiques selon air.system.appearance).
  • toolbar(items) : barre d’outils spécialisée.

Navigation :

  • tab_view(tabs) : onglets.
  • navigation_view(stack) : navigation hiérarchique.

Listes et collections :

  • list(items, row) : liste virtuelle (rendu items visibles seulement).
  • table(items, columns) : table de données.
  • outline(items, children) : arbre hiérarchique.

Modifiers : .font, .color, .padding, .background, .animation, .transition, .accessibility_label, .accessibility_hint, .accessibility_role, .on_press, .on_hover, .draggable, .droppable.

Vues composites de haut niveau (air-ui-views)

Au-dessus des widgets atomiques, air-ui fournit un set de vues composites : composants de granularité grosse implémentant des patterns d’UI standards et fréquents. Le développeur ne réinvente pas un calendrier ou un panneau de paramètres à chaque application : il consomme la vue composite, la configure, profite des conventions cohérentes du système (modèle macOS NSAlert, NSDatePicker, iOS UIAlertController).

Ces vues sont implémentées au-dessus des widgets atomiques et du modèle déclaratif. Elles sont remplaçables (une app avec besoin spécifique écrit le sien avec les briques), mais pour 90 % des cas elles suffisent et garantissent la cohérence.

  • calendar_view(binding, options) : calendrier navigable mois/année, sélection dates. Intègre icu4x pour calendriers locale-aware (cf. ADR-016). Modes : unique, plage, multiple.
  • alert_view(title, message, actions) : dialogue modal alerte/confirmation. Titre, message, icône optionnelle (info/warning/error), 1-3 boutons, modal overlay, theming système (ADR à produire — air.system.appearance / theming).
  • settings_view(sections) : panneau paramètres structuré, modèle macOS System Settings / iOS Settings. Hiérarchie sections/items avec bindings vers préférences (consommées via air-prefs). Layout automatique (sidebar+content sur écran large, navigation hiérarchique sur étroit). Recherche intégrée.
  • help_view(content_source) : visualiseur d’aide intégrée. Navigation hiérarchique, recherche, Markdown ou arbre structuré. Intégration optionnelle avec manuel système global.
  • carousel_view(items, options) : carrousel d’éléments. Navigation swipe/clic, indicateurs de page, animations, défilement auto optionnel. Variant image_carousel pré-configurée.
  • form_view(sections) : formulaire structuré avec validation, modèle iOS form / macOS preference pane. Sections, items typés, validation par item et globale, focus et tab order automatiques.
  • split_view(leading, trailing, options) : vue divisée 2-3 colonnes redimensionnable. Modèle macOS sidebar+content+détail. Adaptation automatique au format d’écran.
  • navigation_split_view(sidebar, content, detail) : extension avec gestion automatique de la navigation hiérarchique en mode compact.
  • media_player_view(source, options) : lecteur multimédia avec contrôles standards (play/pause, scrub, volume, fullscreen). Audio via PipeWire couche 3, vidéo via accélération matérielle VAAPI/V4L2 m2m (cf. ADR à produire — air-screencapture/air-screenrecord).
  • image_view(source, mode) : affichage image enrichi (zoom, pan, loading state, placeholder, error state, lazy loading). Distinct du widget atomique bas niveau image.

Liste indicative amenée à grossir selon besoins observés. Crate air-ui-views distincte d’air-ui-widgets.

Animation déclarative

Cohérent ADR-009, citoyenne de première classe.

#![allow(unused)]
fn main() {
let scale = cx.state(|| 1.0);

button("Click me")
    .scale(scale.get())
    .on_press(move |_| { scale.set(1.2); })
    .animation(.spring(damping: 0.8))
}

Coordination avec air-wm via air.wm.animation (ADR à produire — protocole privé AirCom (catégorie animation)). Mode économie d’énergie peut désactiver/simplifier (cf. futur ADR « theming » reduce_motion, futur ADR « air-power » — mode low_power).

Transitions de vue : fade, slide, scale, personnalisables.

Theming

Cohérent avec le futur ADR « theming ». Consomme air.system.appearance et adapte automatiquement :

  • color_scheme : light/dark.
  • accent_color : couleur d’accent.
  • text_size : facteur taille (Dynamic Type).
  • reduce_motion, reduce_transparency, increase_contrast.

Surcharge possible par l’app : .preferred_color_scheme(.dark).

Accessibilité par construction

Cohérent ADR-017. Toute UI air-ui accessible grâce au substrat impératif air-view-core portant les propriétés universelles.

Génération automatique : button("Save") a automatiquement role=button, label="Save". text("Hello") a automatiquement role=text, value="Hello".

Modifiers explicites pour cas avancés : .accessibility_label, .accessibility_hint, .accessibility_role.

Arbre consommé par air-screenreader via air.wm.accessibility (ADR à produire — protocole privé AirCom (catégorie accessibility), ADR-017).

Crates

air-ui/                — framework graphique (re-exporte air-ui-core + widgets + vues composites)
air-ui-render-gpu/     — renderer Vello
air-ui-widgets/        — widgets atomiques
air-ui-views/          — vues composites
air-ui-animation/      — primitives d'animation

Partie D — air-tui : framework console

air-ui-render-tui : renderer prenant l’arbre de view descriptions et rastérisant en grille de cellules. Layout cellulaire, couleurs (256 ou 24-bit selon backend), attributs (gras, italique, souligné, inversé), clipping et scrolling.

Les trois backends de transport (ADR-008) :

  • Backend 1 : TTY réel avec air-console (plein bypass). Événements via protocole privé AirCom d’air-console. Tous événements bruts (key press/release, modifiers complets, souris, multi-touch). Rendu via grille envoyée à air-console.
  • Backend 2 : Terminal Air sur compositeur graphique (plein bypass équivalent). Terminal Air implémente protocole AirCom privé équivalent. Mêmes capacités, même API.
  • Backend 3 : Terminal tiers ou SSH (mode dégradé). Kitty keyboard protocol si supporté, ANSI standard sinon. Perd events release et souris haute précision.

API Capabilities : l’app interroge ce qui est disponible et dégrade silencieusement selon le contexte.

#![allow(unused)]
fn main() {
if cx.capabilities().has_real_mouse() { mouse_indicator() } else { empty() }
}

Widgets air-tui compatibles air-ui (rendu différent, même API) : text, button, text_field, text_area, toggle, picker, column, row, grid, stack, scroll_view, tab_view, navigation_view, split_view, list, table, outline. Code identique pour app both.

Widgets spécifiques air-tui : ascii_art, progress_bar_text, box(border_style).

Widgets exclus en air-tui : image (placeholder textuel), canvas (impossible — variante canvas_cells pour dessin cellulaire), slider haute précision (version cellulaire à granularité réduite).

Vues composites disponibles en air-tui : alert_view, settings_view, help_view, form_view, split_view, navigation_split_view, calendar_view (variante ASCII type cal). Exclus : carousel_view, media_player_view, image_view (intrinsèquement graphiques).

Animation en TUI : supportée avec contraintes (cellule = unité minimale, framerate typique 30 fps). Transitions par changement progressif de couleur, slide cellulaire. reduce_motion respecté.

I18n et accessibilité en TUI : Unicode complet (CJK double largeur, émojis, scripts complexes), bidi limité (alignement par cellule, pas de mirror automatique de mise en page, mais bidi dans une cellule via cosmic-text), IME tier-1 en backend 1-2 / tier-2 en backend 3, accessibilité via arbre impératif consommé par air-screenreader.

Crates :

air-tui/                  — framework console
air-ui-render-tui/        — renderer cellules
air-tui-widgets/          — widgets standards TUI
air-tui-backend-console/  — backend 1
air-tui-backend-terminal/ — backend 2
air-tui-backend-ansi/     — backend 3

Partie E — air-ui-controller : mélange déclaratif/impératif

ADR-009 : API intermédiaire pour cas nécessitant accès impératif fin (éditeurs graphiques, tableurs, apps média avec timeline, intégration contenu externe).

Conçu citoyen de première classe, pas échappatoire.

#![allow(unused)]
fn main() {
fn editor_view(cx: &mut Cx) -> impl View {
    let canvas_controller = cx.state(|| CanvasController::new());
    
    column((
        toolbar(cx, &canvas_controller),  // déclaratif standard
        air_view_controller(canvas_controller.air_view())  // pont vers impératif
            .min_size(800, 600),
        status_bar(cx, &canvas_controller),
    ))
}
}

Le contrôleur impératif maintient son propre état et gère ses événements indépendamment du cycle déclaratif. La vue est insérée dans l’arbre déclaratif. Interface explicite, localisée, compatible avec le reste (canvas = AirView standard, accessible, themée, observable).

Partie F — Apps multi-modes : graphique ET console

Manifeste de l’app :

[application.ui]
profile = "both"  # ou "desktop", ou "console"

Pour app both : widgets compatibles partout, capability-gate des zones spécifiques.

fn main_view(cx: &mut Cx) -> impl View {
    column((
        text("Mon application"),
        if cx.capabilities().has_graphical_rendering() {
            image("logo.png")
        } else {
            text("[LOGO]").bold()
        },
        list(cx.state(|| vec![...]).get(), |item| item_row(item)),
    ))
}

L’app, packagée .airapp, contient binaire lançable dans les deux contextes. air-launchd détecte le mode et configure le backend.

Cas typiques both : gestionnaires fichiers, lecteurs mail, clients IRC/messagerie, monitors système, configurateurs, éditeurs texte.

Tests

  • Tests unitaires sur logique pure (diff, layout, transformations d’arbres).
  • Snapshot tests : rendu en buffer pixel/cellules comparé avec référence. Détection régressions visuelles.
  • Tests d’accessibilité automatisés : propriétés universelles correctement renseignées pour chaque widget.
  • Tests interopérabilité backends air-tui : trois backends testés sur même app.
  • Tests cross-platform sur machines ADR-014 : rendu identique entre architectures.

Coverage cible : >90% mais pas 100% obligatoire (rendu se valide mieux par intégration que coverage exhaustif).

Choix laissés ouverts pour la spec de composant

  • Algorithme de diff précis (React, Solid, custom) : à évaluer phase 4 selon performance.
  • Set précis des widgets standards (liste indicative, affinement à l’usage).
  • Conventions de nommage des modificateurs (.font_size(24) vs .text_size(24) vs .size_pt(24)) : à standardiser.
  • Profilage runtime de rendu : Raspberry Pi 4 60 fps demande optimisation. Spec détaillera budgets par opération.
  • Stratégie cache view descriptions (pools, arenas).
  • Comportement précis mode “both” en bascule contextuelle (probablement : pas de migration en cours, relance requise).

Section 7 — Couche 5 : Cycle de vie applicatif et services transverses

Rôle et périmètre

La couche 5 est la couche fonctionnelle d’Air visible côté utilisateur. Elle englobe tout ce qui orchestre la vie des applications (lancement, sandboxing, supervision, mise à jour) et tous les services système transverses (notifications, énergie, impression, Bluetooth, partage, réseau, FireWall, etc.). Pattern commun : tous ses composants sont des services système packagés sous forme de bundles .airservice, supervisés par systemd, cloisonnés selon le Principe 10, exposant leurs APIs via AirCom.

Périmètre, trois grandes familles :

  • Cycle de vie applicatif : air-launchd, air-trust, air-appdb, air-update (futur ADR).
  • Services système transverses : air-notifyd, air-power (ADR à produire), air-firewalld (ADR à produire), air-share (ADR à produire — air-share), air-screencapture/air-screenrecord (ADR à produire — air-screencapture/air-screenrecord), air-prefs (ADR à produire — préférences utilisateur), air-keychain (ADR à produire — stockage de secrets).
  • Subsystèmes hardware : air-printd, air-bluetoothd, air-nfcd (ADR à produire — sous-systèmes hardware).

Shell graphique : air-shell, interface visible utilisateur en mode air-desktop.

Vue d’ensemble

┌──────────────────────────────────────────────────────────────────┐
│  Applications utilisateur (.airapp, Flatpak, Unix legacy)        │
└──────────────────────────────────────────────────────────────────┘
                              ▲
                              │ AirCom + xdg-desktop-portal-air
                              │
   ┌──────────────────────────┴──────────────────────────────────┐
   │                                                              │
   ▼                                                              ▼
┌──────────────────────────────────┐         ┌──────────────────────────┐
│  CYCLE DE VIE APPLICATIF         │         │  SHELL GRAPHIQUE         │
│  air-launchd, air-trust,         │         │  air-shell               │
│  air-appdb, air-update           │         │  (air-desktop only)      │
└──────────────────────────────────┘         └──────────────────────────┘
                              ▲                                ▲
                              │ supervision systemd            │
                              │                                │
   ┌──────────────────────────┴──────────────────────────────────┐
   │                                                              │
   ▼                                                              ▼
┌──────────────────────────────────┐         ┌──────────────────────────┐
│  SERVICES SYSTÈME TRANSVERSES    │         │  SUBSYSTÈMES HARDWARE    │
│  air-notifyd, air-power,         │         │  air-printd (CUPS)       │
│  air-firewalld, air-share,       │         │  air-bluetoothd (BlueZ)  │
│  air-keychain, air-prefs,        │         │  air-nfcd (neard)        │
│  air-screencapture/record        │         │                          │
└──────────────────────────────────┘         └──────────────────────────┘
                              ▲
                              │
                              ▼
              ┌─────────────────────────────────┐
              │  Couches 0-4 + systemd          │
              └─────────────────────────────────┘

Partie A — Cycle de vie applicatif

air-launchd : le launcher central

Cohérent ADR-010 et ADR-005.

Rôle. Quand demande de lancement d’une .airapp :

  1. Lit manifeste et entitlements.
  2. Vérifie signature via air-trust.
  3. Crée namespaces (mount, network, PID, user, IPC, UTS).
  4. Applique Landlock pour filesystem selon entitlements.
  5. Applique seccomp-bpf.
  6. Demande à air-registry les capabilities AirCom initiales selon entitlements.
  7. Lance le binaire dans namespace contraint, passe FDs des capabilities via SCM_RIGHTS.
  8. Génère/met à jour unit file systemd user pour supervision.

Modes utilisateur vs système : .airapp standards en systemd --user, .airservice en systemd system avec entitlements étendus.

Activation à la demande : activation = "on-demand" dans manifeste → socket activation systemd. Économise mémoire, accélère boot.

Watchdog et supervision : units systemd avec watchdogs, sd_notify(WATCHDOG=1) attendu des services longue durée.

Cohérence Principe 10 : toutes unit files avec User= dédié (DynamicUser=yes quand applicable), NoNewPrivileges=true, PrivateTmp=true, ProtectSystem=strict, RestrictAddressFamilies= strict, seccomp calculé, capabilities Linux seulement si entitlement le déclare.

API : service AirCom air.system.launcher (launch, running_apps, terminate, status).

air-trust : signatures et notarization

Cohérent ADR-010 (trois niveaux) et ADR-015 (politique ouverte).

  • Stockage local des autorités reconnues.
  • Vérification au lancement : signature développeur, notarization si demandée, conformité politique locale.
  • Mode développeur assouplissant pendant développement.
  • API AirCom air.system.trust.

air-appdb : registre des applications installées

Index local des .airapp et .airservice installées :

  • Scan /Applications, ~/Applications, chemins configurés.
  • Surveillance continue via watcher air-filesystem (couche 1).
  • Métadonnées indexées (identifier, version, icône, locales, services exposés, entitlements).
  • Stockage SQLite local ou TOML indexé (à valider).
  • API AirCom air.system.appdb (find, search, list, observe).
  • Intégration freedesktop : génération .desktop standard pour visibilité depuis GNOME/KDE.

air-update : mise à jour (mention)

ADR futur. Stratégies possibles : OSTree-like (atomique, rollback), Sparkle-like (par app), Flatpak-like.

Partie B — Services système transverses

air-notifyd : notifications système

Premier service livré (ADR-011, phase 2).

  • API AirCom air.system.notifications (send, cancel, observe).
  • Notifications enrichies via air.wm.notifications (ADR à produire — protocole privé AirCom (catégorie notifications)) en air-desktop.
  • Fallback basique en air-base ou sous compositeur tiers.
  • Compatibilité freedesktop via xdg-desktop-portal-air (org.freedesktop.Notifications).
  • Cloisonnement Principe 10.

air-power : gestion énergie (ADR à produire)

Cf. futur ADR — air-power :

  • Service .airservice coordonnant kernel, compositeurs, devices, apps.
  • Trois modes : normal / low_power / performance + mode critique automatique.
  • API AirCom air.system.power.
  • Capabilities air.power.inhibit.sleep, air.power.inhibit.idle, air.power.background_running, air.power.observe.
  • Panneau utilisateur (battery, consommation, historique) dans air-shell.

air-firewalld : FireWall (ADR à produire)

Cf. futur ADR — air-firewalld :

  • Service au-dessus de nftables.
  • API AirCom air.system.firewall.
  • Trois niveaux : entitlements automatiques apps standard, service utilisateur, accès direct nftables (apps .airservice privilégiées avec capability air.system.firewall.admin).

air-share : partage entre appareils (ADR à produire — air-share)

Cf. futur ADR — air-share :

  • Service implémentant AirShare ouvert.
  • Découverte mDNS/DNS-SD + BLE + QR code.
  • Transfert P2P chiffré bout en bout.
  • Authentification TOFU.
  • API AirCom air.system.share via Share Sheet système.
  • Capabilities air.share.send, air.share.receive.

air-screencapture et air-screenrecord (ADR à produire — air-screencapture/air-screenrecord)

Cf. ADR à produire — air-screencapture/air-screenrecord :

  • Capture statique et enregistrement vidéo.
  • Encodage matériel VAAPI/V4L2 m2m.
  • Consentement TCC-style.
  • Indicateur système toujours visible pendant opération.
  • Backend xdg-desktop-portal-air pour Flatpak.

air-prefs : préférences utilisateur (ADR à produire)

Cf. futur ADR — air-prefs :

  • Service AirCom air.system.preferences.
  • Trois domaines hiérarchiques : système (/etc/air/prefs.d/), utilisateur global (~/.config/air/prefs.toml), app (~/.config/air/apps/<app-id>/prefs.toml).
  • Format TOML, schémas typés déclaratifs.
  • Capabilities air.preferences.read.{own-app,global,other-app} + écriture.
  • Observation async, debouncing 100ms par défaut.
  • Versioning des schémas avec migrations déclaratives au lancement.
  • Compatibilité freedesktop via xdg-desktop-portal-air (Settings portal).
  • Cloisonnement Principe 10.

air-keychain : stockage de secrets (ADR à produire)

Cf. futur ADR — air-keychain :

  • Service AirCom air.system.keychain (couche 5 .airservice).
  • Chiffrement Argon2id + ChaCha20-Poly1305/AES-256-GCM via RustCrypto.
  • Plusieurs collections : login (auto-déverrouillée via PAM), high-security (manuelle), session (en mémoire), custom.
  • ACL fines par item (différenciateur vs GNOME Keyring) : allow-always, allow-with-prompt, deny.
  • Capabilities air.keychain.{access.own-items, access.specific-item, manage, admin}.
  • Audit local complet sans transmission externe.
  • Secret Service API exposée via xdg-desktop-portal-air pour Flatpak.
  • Phasage tier-1 (mot de passe + ACL + Secret Service) / tier-2 (biométrie via fprintd) / tier-3 (TPM2, YubiKey FIDO2, smartcards PKCS#11).
  • Cloisonnement strict Principe 10 avec mlock sur master keys déverrouillées.

Partie C — Subsystèmes hardware (ADR à produire — sous-systèmes hardware)

air-printd, air-bluetoothd, air-nfcd : à détailler dans un futur ADR — sous-systèmes hardware.

  • air-printd (tier-1) au-dessus de CUPS, préférence IPP Everywhere driverless, drivers propriétaires HPLIP intégrés Air OS, dialogue d’impression via air-ui, capability air.print.send non sensible.
  • air-bluetoothd (tier-1) au-dessus de BlueZ, UI intégrée, audio Bluetooth délégué PipeWire, HID transparent via evdev, LE Privacy par défaut, capabilities air.bluetooth.{scan,connect,le,admin}.
  • air-nfcd (tier-2) au-dessus de neard, API minimaliste, use case principal authentification, capabilities air.nfc.{read,write}.

Tous respectent Principe 10.

Partie D — Shell graphique air-shell

Interface visible utilisateur en mode air-desktop. Processus séparé d’air-wm (cohérent discipline d’isolation, réévaluation possible si overhead).

Composants visuels :

  • Panneau système : horloge, batterie, réseau, son, Bluetooth, accessibilité active, capture en cours (ADR à produire — air-screencapture/air-screenrecord). Menu utilisateur, recherche système (Spotlight-like).
  • Lanceur d’applications : vue grille (consomme air-appdb), recherche, lancement.
  • Dock (optionnel) : apps épinglées + en cours.
  • Gestionnaire de notifications : affichage entrant (consomme air-notifyd et air.wm.notifications), centre de notifications.
  • Panneau de préférences système : sections (Apparence, Accessibilité, Énergie, Réseau, Bluetooth, etc.). Construit avec air-ui + settings_view (vue composite).

Gestion de session : verrouillage écran (manuel ou auto), authentification au déverrouillage (mot de passe, biométrie si matériel), déconnexion/suspension/arrêt/redémarrage (coordination air-power et systemd-logind), bascule entre sessions multi-utilisateur.

Style et theming : consomme air.system.appearance (ADR à produire — air.system.appearance / theming). Modes clair/sombre/auto, accent color, mode économie d’énergie. Personnalisation via panneau préférences.

Modularité : implémenté comme .airapp particulière avec entitlements étendus (capability air.wm.shell). Théoriquement remplaçable par shell alternatif. En pratique : un shell de référence livré, alternatives possibles communauté.

Partie E — xdg-desktop-portal-air : pont avec freedesktop

Backend Air du portal freedesktop pour apps Flatpak.

APIs portal exposées (backend Air) : FileChooser, ScreenCast/Screenshot, Notifications, Print, Settings, NetworkMonitor, OpenURI, Background, RemoteDesktop (à étudier).

Architecture : daemon D-Bus (exceptionnellement, parce que standard xdg-desktop-portal est D-Bus-based). En interne traduit vers AirCom. L’une des rares utilisations du broker D-Bus par Air, cohérent ADR-005 (D-Bus optionnel mais activé en air-desktop pour compat freedesktop).

Tests

  • Tests unitaires sur logique pure (parsing manifeste, vérification signature).
  • Tests d’intégration en environnement contrôlé (containers avec systemd minimal).
  • Tests interaction inter-services (air-launchd lance app, consomme air-notifyd, notifie air-shell).
  • Tests sur matériel de référence (imprimantes, Bluetooth, mesures consommation).
  • Tests compatibilité freedesktop : xdg-desktop-portal-air testé avec Firefox/LibreOffice/Signal en Flatpak.
  • Audit cloisonnement en CI : vérification automatique unit files respectent Principe 10.

Coverage cible : >85%, pas 100% obligatoire (chemins testables seulement en intégration).

Crates et binaires — récapitulatif

# Cycle de vie applicatif
air-launchd, air-trust, air-appdb, air-update

# Services système transverses
air-notifyd, air-power, air-firewalld, air-share,
air-screencapture, air-screenrecord, air-prefs, air-keychain

# Subsystèmes hardware
air-printd, air-bluetoothd, air-nfcd

# Shell et portail
air-shell (.airapp), xdg-desktop-portal-air

# Outils CLI
air-launch, air-trust-tool, air-share-cli, etc.

Choix laissés ouverts pour la spec de composant

  • Format précis stockage air-appdb (SQLite vs TOML indexé).
  • Politique précise de scan des bundles (fréquence, profondeur, montages externes).
  • Stratégie MAJ .airapp (Sparkle-like, centralisé, ou distribution-driven). Futur ADR.
  • Limites et seuils air-launchd (apps max simultanées, mémoire max par défaut, surcharge).

Section 8 — Aspects transverses

Pourquoi cette section existe

Certaines propriétés d’Air ne tiennent pas dans une couche unique. La sécurité traverse couche 0 (Landlock, seccomp, namespaces), couche 2 (capabilities AirCom), couche 5 (entitlements, sandbox au lancement). L’accessibilité traverse couche 2 (propriétés universelles dans AirObject), couche 4 (génération automatique par frameworks), couche 5 (air-screenreader). Pour comprendre comment ces propriétés se tiennent, regard vertical nécessaire.

Cette section synthétise sept aspects transverses. Pour chacun, rappel de matérialisation à travers les couches, sans répéter le détail.

Sécurité

Propriété émergente de mécanismes consistants à travers toutes les couches.

La sûreté mémoire est le socle. Le userland Rust d’Air est la barrière de sécurité du kernel : le unsafe est concentré en couche 0 (l’interface kernel) et le reste du stack est Rust safe, garanti safe (doctrine Section 1). Les couches sont en outre un rempart qui filtre et détecte au plus tôt (validation en amont, Principe 4) ce qui serait transmis au kernel comme non conforme. Dette de sûreté ouverte : rendre GlobalAlloc (air-alloc) safe dans le cas d’Air, avec l’équipe libs de Rust — voir la Section 2 (« zones unsafe ») et le suivi.

  • Couche 0 : primitives kernel (namespaces, seccomp-bpf, Landlock, cgroups v2, capabilities Linux). Air consomme correctement les mécanismes mûrs du kernel. Seul lieu de unsafe du userland (barrière kernel).
  • Couche 1 : primitives applicatives (air-crypto RustCrypto+rustls, air-filesystem opérations atomiques, pattern privilege separation dans air-process).
  • Couche 2 : modèle capability-based via AirCom (capabilities non-falsifiables FDs Unix sockets, pas d’autorité ambiante, distribution explicite).
  • Couche 3 : compositeurs isolés (air-wm et air-console non-root, accès matériel via logind+seats, capabilities sensibles protégées par entitlements).
  • Couche 4 : apps consomment AirCom, pas kernel directement. Pas d’accès direct matériel/filesystem global/réseau sauf via capabilities accordées.
  • Couche 5 : air-launchd matérialise sandbox au lancement (namespaces, Landlock, seccomp, capabilities AirCom distribuées). Pas d’application possible après lancement.

Principe 10 appliqué à tous services (utilisateurs dédiés, capabilities Linux minimales, hardening systemd, seccomp restrictif, audit CI).

Charte principe 3 (Confiance par défaut) techniquement matérialisée : aucune télémétrie utilisateur, aucun compte obligatoire, aucun cloud forcé. ADR-012-bis durcit télémétrie au strict minimum.

Notions clés : POLA (Principle Of Least Authority), Defense in depth (plusieurs couches d’isolation cumulées).

Internationalisation et localisation (ADR-016)

Traverse toutes les couches car conditionne rendu texte, formatage dates/nombres, saisie, organisation UIs.

  • Couche 1 : AirString Unicode-aware (consomme icu4x), AirPath distinct pour chemins potentiellement non-UTF8.
  • Couche 2 : AirLocale (BCP 47), AirDateTime, AirNumber, AirCurrency locale-aware via icu4x. Cascade fallback macOS-style.
  • Couche 3 : rendu texte bidirectionnel UAX#9 via cosmic-text + HarfBuzz, partagé entre air-wm et air-console. IME via text-input-v3 Wayland. Polices Noto par défaut.
  • Couche 4 : coordonnées logiques leading/trailing avec mirror RTL automatique. Formatage automatique dans widgets text, date_picker, etc.
  • Couche 5 : traductions Fluent (.ftl), plateforme communautaire (Weblate-like) à prévoir.

Langues prioritaires (ADR-016) : anglais, français, allemand, espagnol, italien, portugais, chinois simplifié, arabe, japonais. Couvre 4 systèmes d’écriture distincts qui stressent l’architecture.

Position neutre genre/inclusivité : formes standard + variantes communautaires.

Accessibilité (ADR-017)

Propriété émergente du modèle d’objet C-ABI, pas module ajouté.

  • Couche 2 : modèle d’objet porte propriétés accessibility universelles (label, role, value, state, hierarchy, traits) sur toute AirObject. Service air.accessibility.observer capability-sensible.
  • Couche 3 : air-wm expose air.wm.accessibility agrégeant arbre AirView des apps natives. Pont AT-SPI ↔ AirCom pour compat Orca pendant incubation.
  • Couche 4 : air-ui et air-tui génèrent automatiquement propriétés accessibility depuis structure déclarative. Modifiers reduce_motion, reduce_transparency, increase_contrast consommés automatiquement.
  • Couche 5 : air-screenreader (planifié post-phase 4), air-voice-control (tier-2, Whisper.cpp), magnifier intégré, polices dyslexie-friendly, mode interface simplifiée.

Moteur TTS : piper privilégié, espeak-ng fallback.

Engagement WCAG 2.2 AA pour toutes UIs Air. Tests CI, audit externe avant Air OS 1.0.

Propriété fondamentale : toute UI Air accessible par construction. Distingue Air des écosystèmes où l’accessibilité est rajoutée après coup.

Observabilité et logging

Repose sur intégration native avec journald (cohérent ADR-005) et mécanismes additionnels.

  • Logging structuré universel : AirLog (couche 1) → journald via sd-journal. Format structuré, niveaux standard, contexte attachable. Namespace dédié par service.
  • Audit logs : opérations sensibles (lancement app, octroi capability, capture écran, partage AirShare, NFC, Bluetooth, FireWall) loguées dans journald sous namespaces dédiés.
  • Tracing distribué (AirCom) : propagation trace IDs entre processus. Modèle OpenTelemetry compatible, sans transmission externe (consultation locale via air-trace).
  • Métriques performance : air-power collecte consommation par app via perf_event_open. Panneau utilisateur, aucune transmission.
  • Pas de télémétrie utilisateur : cohérent Charte principe 3 et ADR-012-bis. Aucune remontée externe sauf air-deprecation-tracker strictement opt-in développeur.

ADR futur : stratégie logging et observabilité au-delà journald.

Énergie et performance (futur ADR — air-power, Principe 9)

Coordination verticale par air-power.

  • Couche 0 : configuration CPU governors, états P et C, autosuspend devices via sysfs/netlink.
  • Couche 1 : respect des budgets mémoire et CPU (Principe 9).
  • Couche 2 : AirCom prolonge timeouts en low_power, batch messages non urgents, polling réduit. Transparent pour apps.
  • Couche 3 : air-wm adapte refresh rate (60/30/max selon mode), VRR, animations réduites. air-console adapte similaire.
  • Couche 4 : apps natives consomment AirPowerState observable et réagissent.
  • Couche 5 : air-power orchestre. air-launchd applique politiques énergie déclarées dans manifestes.

Validation continue sur machines modestes (Principe 9) : Raspberry Pi 4 4 Go/8 Go, Mac Intel 8 Go/16 Go.

Principe transversal : Air fluide sur matériel modeste, pas optimisé pour matériel premium. Performance émergeant de sobriété, pas d’optimisation locale.

Confidentialité et données utilisateur

Application concrète Charte (Confiance par défaut).

  • Pas de phone home : aucun composant Air ne fait de connexion sortante par défaut.
  • Pas de cloud forcé : aucun service Air ne nécessite compte cloud. Données utilisateur locales sauf choix explicite de partage.
  • Sandboxing strict : app voit seulement ce que ses entitlements accordent. Pas d’accès home par défaut. Pas d’accès contacts/calendrier/micro/caméra sans capability + confirmation TCC-style.
  • Audit local : opérations sensibles loguées (journald), pas transmises.
  • Pas de tracking inter-app : une app ne peut savoir quelles autres apps installées (sauf entitlement explicite). Pas d’identifiant utilisateur partagé.
  • Données chiffrées au repos : keychain (air-keychain) chiffre secrets. Disque entier chiffrable (LUKS au niveau Air OS).

Stabilité contractuelle (ADR-012, Principe 8)

Outillée et testée en continu.

  • Trois zones de stabilité : air-stable (10 ans ABI), air-internal (SemVer source), air-experimental.
  • Outils mainteneurs : air-abi-check, air-symver, air-deprecation-tracker. Intégrés dès phase 0.
  • Tests de conformité ABI : binaires de référence testés en CI contre versions postérieures.
  • Format .airapp stable : manifest-version lu de v1 à actuel.
  • Schémas AirCom stables : règles d’évolution Cap’n Proto strictes.
  • Noms d’entitlements stables : jamais renommés ni retirés.
  • Période 0.x exploratoire de 12-24 mois après phase 4 avant fixation air-stable définitive (modèle Rust).

Synthèse : ce qui fait la cohérence d’Air

La cohérence d’Air ne vient pas d’un composant central magique. Elle vient de disciplines consistantes appliquées dans toutes les couches :

  • Le modèle d’objet C-ABI (ADR-002) — toute structure inspectable, bindable polyglottement.
  • AirCom (ADR-001) — capability-based partout.
  • Les frameworks air-ui et air-tui (ADR-008, ADR-009) — conventions système (theming, accessibility, i18n) automatiques.
  • systemd (ADR-005) — supervision unifiée pour tous les services.
  • Les Principes d’ingénierie — appliqués à tout le code.
  • La Charte — valeurs vérifiables techniquement.

Aucune partie d’Air n’est cohérente toute seule. Toutes le sont parce qu’elles partagent les mêmes disciplines. C’est cette consistance qui doit faire d’Air un système crédible plutôt qu’un assemblage de bonnes idées.


Section 9 — Contrats inter-couches et règles d’isolation

Pourquoi cette section existe

Les couches d’Air sont matérialisées par des contrats publics stables entre voisines et par des règles d’isolation strictes. Cette section formalise ces contrats et règles. Leur respect n’est pas qu’une bonne pratique : c’est ce qui permet à Air d’évoluer sans casser les contrats sur 10 ans (cohérent ADR-012, Principe 8), de remplacer une couche si nécessaire sans toucher aux autres, et de garder la complexité localisée.

Principe général : consommation strictement descendante

Une couche N ne peut consommer que les contrats publics de la couche N-1.

Implications :

  • Pas d’accès direct au kernel depuis couches ≥ 2. Apps consomment AirFile, AirMemory de la couche 1.
  • Pas d’appel direct systemd ou D-Bus depuis couches ≥ 3. Consommation via air-systemd couche 2.
  • Pas de couplage applicatif au compositeur spécifique. Apps consomment air.wm.session (ADR à produire — protocole privé AirCom) et s’adaptent.
  • Pas d’accès direct matériel depuis couches ≥ 4. Passage par couche 3 ou primitives couche 1.

Le saut de couches : autorisé exceptionnellement

Un service couche 5 peut consommer directement couche 0 ou 1 sans passer par intermédiaires, dans cas justifiés :

  • air-power consomme air-sys-syscall (couche 0) pour CPU governors via sysfs et perf_event_open.
  • air-firewalld consomme air-sys-syscall pour netlink/nftables.
  • air-bluetoothd consomme libudev (couche 1) pour énumérer adaptateurs avant BlueZ.

Règle d’usage : nommé explicitement en documentation du composant, justifié techniquement, soumis à audit. Pas le cas général. Trop de sauts = signal qu’il manque une abstraction couche intermédiaire.

Le passage transverse : interdit

Une couche N ne peut consommer une autre couche N sans passer par une couche inférieure commune. Exemple : air-bluetoothd ne consomme pas directement air-printd. S’ils ont besoin de coordonner, passage par AirCom (couche 2) et exposition mutuelle de services.

Évite les couplages horizontaux fragiles. Force à exposer des APIs propres.

Le passage ascendant : strictement interdit

Une couche N ne peut jamais consommer N+1 ou au-dessus.

  • Couches basses ignorent qui les consomme.
  • Inversion de dépendance par callbacks : couche basse expose API d’abonnement (observe), couche haute s’abonne.

Contrats inter-couches : formalisation

Pour chaque frontière :

  1. APIs publiques exposées : types, fonctions, services AirCom déclarés stables. Documentés.
  2. Garanties de comportement : pré-conditions, post-conditions, invariants, sémantiques.
  3. Engagement de stabilité : selon zone (air-stable, air-internal, air-experimental). Majorité air-stable (10 ans).
  4. Mécanismes d’évolution : ajout sans casse (nouveaux champs, méthodes, capabilities), retrait via deprecation 5 ans + alternative.

Synthèse des frontières principales

  • Couche 0 → Couche 1 : API Rust idiomatique sans unsafe exposé. Interne : seule la couche 1 (et les développeurs du système Air) s’y appuie ; instable, elle suit le kernel.
  • Couche 1 → toits : API Rust idiomatique via les Managers de domaine (ADR-077). Interne, non contractuelle pour les apps. Consommée par les toits libair_c (libc C) et PAL std. L’ex-libair-base.so est retirée.
  • Couche 2 → Couches 3-5 et développeurs : la lib Rust libair (surface publique stable) + les ABI C du modèle d’objet (libair-object.so), AirCom (libair-com.so), event loop (libair-event.so). Stable 10 ans. Contrat le plus important du système.
  • Couche 3 → Couches 4-5 : Wayland standard + AirCom privé (air.wm.* ADR à produire — protocole privé AirCom) graphique. Protocole privé air-tui ADR-008 console. PipeWire audio.
  • Couche 4 → Couche 5 : API frameworks consommée par apps. Pas frontière au sens strict (app inclut framework), mais contrat conceptuel.
  • Couche 5 → utilisateurs et apps externes : services AirCom (air.system.*), backend xdg-desktop-portal-air, format .airapp et .airservice.

Cas particuliers

Interface avec systemd : systemd pas couche Air. Composant externe. Encapsulé dans air-systemd (couche 2). Toute autre couche passe par air-systemd. Si Air doit modifier dépendance systemd un jour, point unique.

Interface avec D-Bus : optionnel pour Air (ADR-005). Encapsulé dans air-dbus-bridge (couche 2). xdg-desktop-portal-air (couche 5) est l’exception qui parle D-Bus de bout en bout (parce que standard xdg-desktop-portal est D-Bus-based).

Audit du respect des règles

En CI :

  • Analyse dépendances de crates : aucune crate couche N ne dépend de couche supérieure. Vérifié via cargo metadata.
  • Analyse appels AirCom : capabilities consommées correspondent aux contrats exposés par couches inférieures autorisées.
  • Audit manuel sauts de couches : à chaque revue de code, auteur justifie explicitement tout saut dans commit message.

Section 10 — Évolution et stabilité

Pourquoi cette section existe

Air conçu pour exister sur 10 ans minimum (Charte principes 4 et 5). Stabilité contractuelle (ADR-012, Principe 8) outillée, testée en continu, matérialisée par disciplines de versioning. Cette section synthétise comment Air évolue dans le temps en gardant ses promesses.

Les trois zones de stabilité (rappel ADR-012)

  • air-stable : APIs publiques engagées sur 10 ans à partir d’Air 1.0. ABI C stable via versioned symbols GNU/Linux. Schémas AirCom stables avec règles d’évolution Cap’n Proto. Format .airapp versionné (v1 à actuel pour toujours). Noms d’entitlements stables. Majorité des contrats inter-couches.
  • air-internal : APIs internes au stack, partagées entre composants Air mais pas consommées par apps. SemVer source dans un release majeur. Évolution plus libre à chaque majeur.
  • air-experimental : aucune stabilité. Marqué #[experimental] ou crates air-experimental-*. Pour nouvelles fonctionnalités en exploration.

Correspondance couche ↔ zone (structurante). Les couches 0 et 1 sont air-internal : la couche 0 suit le kernel (elle ajoute/déprécie au rythme de Linux et n’a pas vocation à implémenter tous les syscalls, surtout le legacy), et la couche 1 — ses Managers — évolue avec lui (adopte les features modernes, déprécie/abandonne le legacy). Aucune des deux n’est un contrat pour les développeurs d’applications, qui ne doivent pas s’y appuyer ; Air se réserve d’en faire évoluer le comportement. Le sceau couche-0-v1.x/couche-1-v1.x garantit une stabilité interne (les toits d’Air lient une couche 1 figée), pas un contrat public. La zone air-stable — le contrat public 10 ans — est portée par la couche 2 : la lib Rust libair et les ABI C du modèle d’objet. La libc libair_c (toit) vise, elle, la conformité au kernel Linux (pas POSIX) et sa propre discipline de dépréciation des symboles hérités.

Le phasage long terme (ADR-011)

  • Phase 0 : air-sys-types + air-sys-syscall + air-base-lib (couches 0-1 partielles). Coverage 100%, tests cross-architecture. Apprentissage.
  • Phase 1 : modèle d’objet + AirCom + event loop (couche 2). Bindings polyglottes émergent.
  • Phase 2 : premier livrable publiable air-base 1.0. Profil console, services système basiques.
  • Phase 3 : composition + rendu + audio + input (couche 3). air-wm et air-console opérationnels.
  • Phase 4 : frameworks UI (couche 4) + services desktop. air-desktop 1.0 publiable.
  • Phase 5+ : enrichissement progressif, écosystème, communauté, Air OS distribution.

Où en est-on (2026-07-10). Les phases 0-1 sont largement réalisées : couches 0 et 1 scellées (couche-0-v1.12 / couche-1-v1.9), et le jalon toolchain M5 est atteint — le std Rust tourne sur la libc d’Air (libair_c), zéro glibc, sur les 2 arches. La production courante vise air-sshd (ADR-074) et la consolidation des toits (libair_c, PAL std) et de la surface publique Rust libair (couche 2).

Période 0.x exploratoire de 12-24 mois après phase 4 avant fixation air-stable définitive.

Mécanismes d’évolution sans rupture

  • Ajout : toujours autorisé (nouvelle méthode, champ optionnel, capability, widget, propriété observable).
  • Suppression : deprecation period minimum 5 ans. Annonce publique, marquage #[deprecated], alternative documentée, air-deprecation-tracker mesure l’usage réel.
  • Modification comportement : traité comme suppression + ajout. Pas de “fix silencieux”.
  • Versioning sémantique schémas AirCom : nouvelle version d’interface (V2) coexistant avec ancienne (V1). Migration progressive.
  • Migration formats données : champ version lu dès le début, migration automatique. Anciens formats lisibles.

Outils mainteneurs

  • air-abi-check : analyse ABI C, compare à référence, détecte ruptures. CI à chaque commit.
  • air-symver : gère versioned symbols GNU/Linux pour libair-*.so.
  • air-deprecation-tracker : collecte usages APIs deprecated via télémétrie strictement opt-in développeur (cohérent ADR-012-bis). Permet de quantifier objectivement quand retirer une API. Données auditables, jamais identifiantes.
  • air-com-schema-check : valide qu’évolution schéma respecte règles Cap’n Proto.
  • air-manifest-check : valide manifestes .airapp/.airservice syntaxiquement et entitlements déclarés existants.

Développés dès phase 0, raffinés au fil du temps. Disponibles à la communauté.

Gouvernance de l’évolution (ADR-015)

  • Phases 0-2 (BDFL) : fondateur tranche. Décisions structurantes consignées comme ADRs.
  • Phases 3-4 (TC émergent) : Technical Committee émerge. Nouveaux ADRs revus par TC avant ratification BDFL.
  • Phase 5+ (gouvernance distribuée) : Fondation indépendante (modèle Blender). Évolutions contrats air-stable via RFC publique avec période de commentaires.

Communication des évolutions

  • Release notes structurées : par catégorie (nouveautés stable, changements internal, expérimentations, deprecations annoncées, deprecations retirées).
  • Calendrier de deprecations public : table avec APIs deprecated, date d’annonce, date prévue retrait minimum, alternative.
  • Migrations documentées : pour chaque deprecation, document step-by-step.
  • Pas de surprises : utilisateurs et développeurs anticipent plusieurs années à l’avance.

Engagement temporel concret

À partir d’Air 1.0 (phase 4 + 12-24 mois période 0.x) :

  • Contrats air-stable garantis sur 10 ans minimum.
  • ABI C versioned symbols : aucune rupture sans deprecation 5 ans + version majeure suivante.
  • Format .airapp : lisible v1 à actuel pour toujours.
  • Services AirCom air.system.* : compatibles ascendant pour toujours dans la branche d’interface.
  • Entitlements : pas de renommage, pas de retrait sans deprecation 5 ans.

Engagements publics consultables. Violer = échec public connu. Pression que le projet s’impose.

Cas particulier : période avant 1.0

Pendant phases 0 à 4 et période 0.x exploratoire, Air pas stable. Explicite. Développeurs adoptant tôt acceptent ruptures occasionnelles, peuvent influencer les choix avant fixation.

À 1.0, ce qui est dans air-stable se fige pour 10 ans. Commit fort qu’il faut être prêt à tenir.

Synthèse : la stabilité comme propriété émergente

Stabilité 10 ans pas un vœu pieux. Propriété émergente :

  • Trois zones différenciées rendant explicite ce qui est figé.
  • Outils mainteneurs automatisant vérifications.
  • Mécanismes d’évolution sans rupture (ajout OK, retrait via deprecation longue).
  • Gouvernance évoluant avec le projet sans casser engagements.
  • Communication transparente.
  • Engagement public consultable.

Air ne peut pas se permettre de casser ses contrats. Sa cible (utilisateurs migrant macOS/Windows, communautés institutionnelles long horizon) attend une plateforme professionnelle. La stabilité est un investissement, pas une option.


Licence du document : MPL 2.0
Statut : Document fondateur immuable. Toute évolution requiert un RFC dédié.