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

Spec couche 1 — air-config (compilateur de configuration + artefact Cap’n Proto)

Spécification technique — Version 0.2 (décisions #1–#5 validées BDFL 2026-06-25 ; grammaire de source + schémas .capnp à concevoir avant implémentation). Couche 1 « Primitives système ». Crate DÉDIÉE (pas dans air-base-lib).

Position et méthode

air-config est le cœur de configuration d’Air : il transforme une source typée (éditée par l’humain ou une GUI) en un artefact binaire reproductible que le runtime consomme, et projette vers les fichiers texte attendus par le monde C-Unix. C’est un composant sécurité-critique (il ingère de la donnée humaine = donnée hostile, Principe 3) → rigueur couches 0/1 : couverture 100 %, fuzzing obligatoire, property-based, aucune fn unsafe exposée, arithmétique défensive, nommage ADR-029.

Crate dédiée (décision ADR-040). air-config n’est pas dans air-base-lib : elle tire Cap’n Proto (cf. §dépendances), et air-base-lib — la crate la plus consommée — doit rester légère. La §3 d’air-base-lib-services (vieux AirConfig TOML/serde) est superseded par cette spec.

Décisions de socle déjà gravées (NON rouvertes ici)

  • ADR-033 — modèle : source typée (canonique)compilateur validantartefact binaire ; texte = projection, jamais la forme runtime ; générations + switch atomique + rollback ; backends d’émission.
  • ADR-040 — l’artefact est en Cap’n Proto ; addendum : capnpc exige le tool C++ au build → politique code généré committé (schémas .capnp + .rs généré versionnés ; tool C++ = opération mainteneur, hors CI).
  • ADR-041 — backends /etc : projections générées, lecture seule sélective, air-config seul écrivain (swap atomique) ; mutables-runtime → /run ; /etc/systemd/system possédé par le backend systemd.

Vue d’ensemble (le flux)

   SOURCE TYPÉE (canonique, versionnable, éditée humain/GUI)
        │   air-config-compile : VALIDE en amont (schéma + types + invariants)
        │   diagnostics qualité compilateur (ligne/col, message actionnable)
        ▼
   ARTEFACT Cap'n Proto (binaire, reproductible, version + checksum)
   ├──────────────► RUNTIME Air : lecture ZÉRO-COPIE / mmap (jamais de /etc)
   └──► BACKENDS d'émission ──► /etc/* générés (libc/Unix), RO sélectif, /run pour mutables
   (sens inverse : IMPORT  /etc existant → source, au bootstrap/migration)

Section 1 — Le schéma (Cap’n Proto)

La structure de la configuration est décrite par des schémas .capnp (versionnés, numérotation de champs = évolution avant/arrière par construction). Le code Rust de (dé)sérialisation est généré par capnpc et committé (ADR-040 addendum). Chaque domaine de config (réseau, services, locale…) a son schéma ; un en-tête commun porte version de schéma + checksum (corruption détectée à l’ouverture, jamais lue en silence).

Section 2 — Le compilateur : air-config-compile

Cœur sécurité-critique (ADR-033 §3). Pipeline pur (sans-IO sur la validation) :

parse(source) → AST typé → valide(schéma + types + invariants) → encode(Cap'n Proto)
  • Validation en amont (Principe 4 / « parse, don’t validate ») : schéma, types, invariants métier ; refuse toute entrée invalide avant de produire l’artefact. Le runtime ne voit jamais de config invalide.
  • Diagnostics qualité compilateur : ligne/colonne, message actionnable, suggestion (ADR-019 : AirError riches/contextualisés).
  • Entrée hostilefuzzing obligatoire sur le parseur de source + le décodeur d’artefact ; property-based ; 100 % couverture.

DÉCISION #1 (VALIDÉE) — syntaxe de la source = format maison minimal, hand-rolled, zéro-dép, fuzzé. La « source typée » est un texte validé contre le schéma Cap’n Proto, parsé par un parseur maison minimal (pas de toml/serde → reste zéro-dép, cohérent ethos ; même discipline que les parseurs DNS/glob déjà hand-rollés + fuzzés). Reste à concevoir : la grammaire concrète de ce format (passe de design dédiée avant implémentation — petit format typé clé/valeur/sections, diagnostics ligne/colonne).

Section 3 — Le lecteur runtime

#![allow(unused)]
fn main() {
/// Ouvre un artefact compilé en lecture ZÉRO-COPIE (mmap via couche 0 `mem`).
/// Vérifie version de schéma + checksum ; corruption → AirError (jamais de lecture muette).
pub struct AirConfig { /* mmap de l'artefact + accès Cap'n Proto */ }

impl AirConfig {
    /// # Errors `InvalidData` (version/checksum), `NotFound`.
    pub fn open(path: &AirPath) -> AirResult<Self>;
    /// Accès typé zéro-copie aux domaines (généré depuis le schéma).
    pub fn reader<T: CapnpRoot>(&self) -> AirResult<T::Reader<'_>>;
}
}
  • Zéro-copie / mmap (ADR-033 §4, Principe 9) : pas de parse au runtime, lecture par offsets — gain réel au boot (Pi). Le runtime Air-natif lit l’artefact, jamais /etc.

Section 4 — Générations, switch atomique, rollback (ADR-033 §6)

  • Conserver la dernière génération known-good ; basculer atomiquement (air-filesystem::write_atomic) ; retour à la génération précédente (y compris une entrée de secours au boot pour le cas pré-userspace). La validité ne suffit pas — une config valide peut exprimer une mauvaise intention ; le filet anti-brick est le modèle générationnel.

Section 5 — Résolution de chemins (XDG)

Repris de la partie utile de l’ancien air-base-lib §3 (pure construction de chemins) : AirConfigPaths::for_component(name) → cascade $XDG_CONFIG_HOME / ~/.config/<name> / $XDG_CONFIG_DIRS / système (système < utilisateur). Lecture effective via air-filesystem.

Section 6 — Backends d’émission /etc + import (ADR-041)

  • Émission : air-config est le seul écrivain des fichiers /etc qu’il possède (générés depuis la source, swap atomique). Backend systemd (possède /etc/systemd/system : *.wants, drop-ins), backend resolv.conf/hosts/nsswitch, etc. Mutables-runtime → symlinks /run. La protection (montage RO sélectif) est une décision couche 5 / intégration OS (hors air-config, qui génère).
  • Import inverse (bootstrap/migration) : lire /etc existant → source Air (avant d’émettre). Périmètre de possession explicite (tout /etc n’appartient pas à Air).

DÉCISION #2 (VALIDÉE) — backends v1, séquencés par risque croissant :

  1. resolv.conf + hosts (simples, quasi-statiques — bons premiers backends) ;
  2. systemd (/etc/systemd/system : *.wants/drop-ins — activation de services) ;
  3. passwd (+ shadow) en dernier : c’est la base de comptes, la plus sensible — shadow porte les hashes (secrets → zeroize, jamais loggés, manipulation extra-prudente ; interaction PAM/login). Traité avec le plus grand soin, après que les 3 premiers backends soient éprouvés. Périmètre progressif (ADR-041) ; d’autres backends s’ajouteront ensuite.

Crates / structure — 3 crates (DÉCISION #3 validée)

air-config-schema/   — schémas .capnp + code Rust GÉNÉRÉ (committé, ADR-040) + en-tête version/checksum
air-config-compile/  — le compilateur (parseur source maison → valide → émet Cap'n Proto) [sécurité-critique]
air-config/          — façade : lecteur runtime (mmap zéro-copie), AirConfigPaths, générations/rollback,
                       ET les backends d'émission /etc + import inverse (modules internes)

Les backends sont des modules d’air-config (pas une 4ᵉ crate) → 3 crates au total.

Dépendances (règle des 80 %, ADR-024)

  • capnp (runtime, zéro dépendance, pur Rust — vérifié) + capnpc (build-time, code généré committé ADR-040 → tool C++ hors CI ; exception 80 % partagée AirCom).
  • air-base-lib (erreurs/chemins), air-filesystem (lecture/écriture atomique), couche 0 mem (mmap). Parseur de source : selon décision ouverte #1 (idéalement zéro-dép). Aucune dépendance systemd (le backend systemd écrit des fichiers).

Stratégie de tests

  • Couverture 100 % ; fuzzing : parseur de source (hostile) + décodeur d’artefact + import /etc. Property-based : round-trip source→artefact→lecture ; cascade XDG ; générations (rollback rétablit l’exact known-good). Intégration : compile une config réelle, mmap, relit ; émet /etc puis ré-importe (idempotence) ; corruption d’artefact → erreur (jamais lecture muette). Reproductibilité (ADR-025) : même source → artefact bit-pour-bit (gate repro) ; encodage Cap’n Proto déterministe.

Décisions ratifiées (BDFL 2026-06-25)

  1. Syntaxe source = format maison minimal, hand-rolled, zéro-dép, fuzzé (§2 ; grammaire concrète à concevoir avant impl).
  2. Backends v1 séquencés : resolv.conf+hosts → systemd → passwd/shadow en dernier (§6).
  3. 3 crates : air-config-schema + air-config-compile + air-config (backends = modules d’air-config).
  4. GUI contrainte par schéma : DIFFÉRÉE (un frontend ultérieur ; le cœur validant reste le même).
  5. Tool C++ capnp : version pinnée (ADR-025), confiné à la régénération mainteneur (mode prébuilt vs source à fixer à l’impl).

Pré-implémentation restante : concevoir la grammaire du format de source (#1) et définir les schémas .capnp des domaines v1.

Différé

  • Frontend GUI ; backends /etc au-delà du périmètre v1 ; surface ABI C d’air-config (si pertinente) ; intégration air-network/air-systemd (couche 2) consommant la config.

Licence du document : MPL 2.0 Statut : v0.2 — crate dédiée couche 1, sécurité-critique. Décisions #1–#5 validées. Restent à concevoir avant implémentation : la grammaire du format de source (maison, fuzzé) et les schémas .capnp des domaines v1. Bases : ADR-033 / ADR-040 (+ addendum) / ADR-041.