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-launchd en couche 2 — note de cadrage et découpage

Note de conception. Version 1.0 (2026-08-04). Relevé dans l’arbre le même jour.

Décision cadrée ici (BDFL, 2026-08-04) : air-launchd est un composant de couche 2, et il est requis dès la v1.0. Cette note dit pourquoi le placement change, ce que la vérification a trouvé — beaucoup plus que prévu —, et en trois temps ce qu’il reste à faire.

1. Pourquoi le placement change

L’hypothèse d’origine, et ce qu’on a appris depuis

Aux premières heures du projet, on espérait — avant d’avoir mesuré la complexité de systemd — lui faire porter la mise en cage et le sandboxing selon nos critères. air-launchd était rangé en couche 5, dans la famille « cycle de vie applicatif » de la macro-architecture, aux côtés d’air-trust, air-appdb et air-update.

Cette hypothèse est tombée. Seul un lanceur à nous peut mettre en œuvre notre niveau de sécurité — l’arbitrage manifeste/octroi d’ADR-150, la cage posée avant de rendre la main, l’enveloppe bornée par l’image. Et s’appuyer sur systemd a, au-delà du sandboxing, trop d’implications structurelles.

Le placement en couche 5 était devenu intenable

ADR-149 D1 pose qu’une couche N n’invoque que les opérations de la couche N-1, sans exception — et a supprimé la section « saut de couches autorisé exceptionnellement » de la macro-architecture.

Or air-launchd a besoin d’air-sandbox et d’air-process (couche 1) et d’AirCom (couche 2). Depuis la couche 5, ce sont des sauts de trois et quatre étages. En abolissant l’entorse, on avait rendu impossible le placement du composant qui a le plus besoin de descendre.

La couche 2 résout cela sans exception, et il y a un précédent exact

air-sshd est déjà un démon de couche 2, et il lie exactement ce dont air-launchd aura besoin : air-sandbox, air-process, air-filesystem, air-socket, air-log, air-account, air-keystore — toutes en couche 1, donc N-1. Aucun saut.

Mais l’argument décisif est ailleurs :

La couche déclare ce qu’un composant a le droit de lier, pas qui a le droit de lui parler. Les dépendances AirCom sont des dépendances d’exécution, pas d’édition de liens. air-wm (couche 3) ne dépend pas de la crate air-launchd — il dialogue avec le démon. Le gate check-layers ne lit que le graphe de crates ; il ne voit pas, et n’a pas à voir, qui appelle quel service.

La couche 2 est décrite comme « le cœur conceptuel […] les services fondamentaux que toute couche supérieure consomme ». Un lanceur est exactement cela.

Ce que ce déplacement ne résout pas

La formulation d’ADR-149 D1 reste fausse pour la couche 5. ADR-006 (« Profils de déploiement ») pose que le profil air-base a « couche 3 absente ; couche 4 absente ; couche 5 présente » : la couche 5 y repose directement sur la couche 2. « N n’invoque que N-1 » n’est donc pas vrai littéralement — les étages 3 et 4 sont escamotables.

Le problème est repoussé, pas résolu, et il est aujourd’hui latent : l’arbre ne compte aucune crate en couche 3, 4 ou 5 (2 en couche 0, 33 en couche 1, 45 en couche 2). Le gate n’a jamais eu l’occasion d’y trébucher. À amender au premier vrai composant de couche 5 ; plus en urgence.

2. Ce que la vérification a trouvé — la couture est livrée, pas seulement conçue

ADR-093 §6 annonçait un trait ServiceHost dont « la v1 est une implémentation systemd, la v2 fournira une implémentation air-launchd du même trait ».

Ce n’est pas resté sur le papier. La crate air-service existe, en couche 2, et se décrit elle-même comme « le seam de supervision v1(systemd)→v2(air-launchd) » et « une fonction de forçage : tout démon Air, pas seulement air-sshd, le réutilise plutôt que de câbler systemd en dur ». Elle livre :

SymboleRôle
trait ServiceHostLe contrat : d’où vient le socket d’écoute, comment notifier l’état, comment recevoir l’ordre d’arrêt
struct SystemdHostL’implémentation v1
struct ShutdownSignalLa source d’arrêt armée
parse_listen_fds, parse_notify_socket, format_notify_message, send_notificationLes briques des deux protocoles systemd, en Rust sûr, sans binding libsystemd

Le contrat tient en trois méthodes : take_listeners() — reprend les sockets hérités, liste drainée pour interdire la double adoption —, notify(state) et l’armement de l’arrêt.

Et il est prouvé par un test : crates/air-sshd/tests/seam.rs vérifie que SystemdHost notifie READY contre un socket bouchon.

Conséquence : le premier temps de la roadmap n’est pas de concevoir une abstraction, c’est d’écrire une seconde implémentation d’un trait à trois méthodes déjà éprouvé.

3. Le découpage en trois temps

Temps 1 — le lanceur minimal, superviseur de services

Écrire AirLaunchdHost: ServiceHost à côté de SystemdHost, et le démon qui va avec : socket-activation, notification de disponibilité, arrêt propre.

Critère de sortie, binaire et mesurable : air-sshd démarre sous air-launchd au lieu de systemd, sans qu’une ligne d’air-sshd change. Le trait rend cette affirmation vraie ou fausse, jamais approximative.

Ce temps ne dépend d’aucune décision en attente. C’est ce qui le rend prioritaire : il est faisable immédiatement, et il transforme air-launchd d’intention en exécutable.

Temps 2 — la mise en cage à l’exec

L’arbitre d’ADR-150 : signature → octroi → enveloppe, puis pose de la cage air-sandbox avant de rendre la main. C’est ici que se joue « notre niveau de sécurité » — précisément ce que systemd ne sait pas faire, et la raison pour laquelle l’hypothèse d’origine est tombée.

Bloqué par la question ouverte n° 1 d’ADR-150 : la largeur des enveloppes par défaut. Le mécanisme de cage existe (ADR-112, fait, en production dans air-sshd) ; ce qui manque est de savoir quoi poser.

Temps 3 — la supervision réelle : dépendances, ordre, et cgroups

Dépendances entre services, ordre de démarrage, politique de redémarrage — et surtout les cgroups, qui sont ce qui remplace vraiment systemd plutôt que de l’imiter :

  • tuer un service de façon fiable (cgroup.kill) — un service ne s’échappe pas par un double fork ;
  • compter ce qu’il consomme, exactement ;
  • attribuer ses journaux sans qu’il puisse mentir sur son identité ;
  • borner les forks (pids.max), donc la bombe à fourche.

État de la matière — corrigé le 2026-08-15. Cette note affirmait qu’« aucun AirCgroupManager n’existe ; seul air-sys-syscall connaît le mot ». C’était vrai à la rédaction, ce ne l’est plus, et une note qui vieillit sans le dire égare autant qu’une note fausse. crates/air-cgroup existe et porte AirCgroupManager, AirCgroupDelegation et AirCgroupdelegate, enable_for_children, set_process_limit, set_memory_high, kill_all, processes — et air-launchd s’en sert (supervise.rs) : cgroup par service, déménagement du superviseur, pids.max et memory.high posés sans être demandés, mise à mort de l’arbre par cgroup.kill.

La prévision de placement, elle, s’est vérifiée : ce n’est pas un descellement de couche 0 — les cgroups se pilotent par le système de fichiers (/sys/fs/cgroup), donc par le manager de fichiers. C’était bien un Manager de couche 1 à écrire, pas des syscalls à envelopper.

Ce qui reste du temps 3 après ce constat n’est donc pas le bornage, mais les dépendances, l’ordre, le sens de « prêt » et l’arrêt ordonné — ADR-161.

4. Ce que cette note ne tranche pas

  1. La largeur des enveloppes par défaut — bloque le temps 2, et reste la question ouverte n° 1 d’ADR-150.
  2. Le registre de noms et la distribution de la capability initiale par le lanceur (ADR-010, POLA strict). C’est ce qui rendrait le rendez-vous AirCom trivial pour une application ; cf. notes/aircom-surface-rust-sure-conception-fr.md.
  3. Le sort de systemd après le temps 1. Deux implémentations du même trait peuvent coexister longtemps ; décider si SystemdHost reste, et jusqu’à quand, n’est pas fait ici.
  4. La formulation d’ADR-149 D1 pour la couche 5, repoussée ci-dessus.

Licence du document : MPL 2.0 Statut : note de conception. Cadre un découpage ; les décisions engageantes vivent dans les ADR.