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

ADR-100 — Base comptes/credentials binaire (/etc/air/{passwd,shadow,group}) : source de vérité Air, projection /etc pour le monde C, CLI d’administration unique

Statut : Accepté (2026-07-24, décision BDFL). RFC de structure (ADR-015). Companion d’ADR-067 (la crate air-account), d’ADR-073 (doctrine de configuration binaire) et d’ADR-041 (coexistence /etc : source Air canonique, projection générée pour le monde C). Réutilise le précédent de schéma bespoke (ADR-096, enveloppe AIRCFGv1 + checksum FNV) éprouvé par le sous-système de configuration air-ssh/air-sshd (ADR-099).

Catégorie : Architecture. Descellement additif de la couche 1 scellée (air-account, couche-1-v2.3couche-1-v2.4) — aucune signature publique ne change, donc aucune rupture d’ABI. Ajoute une crate schéma couche 1 (air-account-schema) et un exécutable d’administration couche 2 (CLI unique).

Contexte

La gestion de compte Unix/Linux d’Air repose aujourd’hui sur air-account (couche 1, [ADR-067]), qui lit et parse /etc/passwd, /etc/group, /etc/shadow en texte. C’est le point de médiation unique : toute la surface compte des toits d’Air en dépend —

  • la libc C-ABI (libair_c) : air-libc-pwd (getpwnam_r/getpwuid_r), air-libc-grp (getgrnam_r/getgrgid_r), air-libc-shadow (getspnam_r), air-libc-id/initgroups sont de purs binders qui appellent air_account::lookup_user_by_name/uid, lookup_group_by_name/gid, groups_of_user, shadow_by_name ;
  • la std Rust portée (target_os="air") : air_account fait partie de l’ensemble build-std lié par la std sur os=air.

Or la doctrine de configuration binaire ([ADR-073]) exige que la config et les données de configuration d’Air soient binaires, stockées sous /etc/air/, éditées par un CLI dédié (jamais « à l’éditeur de texte »). La base comptes est une donnée de configuration système : elle doit donc devenir binaire.

Cette évolution est aussi le substrat manquant d’[ADR-073 §2] et du gate « mot de passe administrateur » différé par [ADR-099 §5] : le mot de passe administrateur est une entrée shadow. Tant que le stockage shadow binaire n’existe pas, le gate ne peut être bâti. Ce chantier le débloque.

La gestion de compte Unix couvre deux domaines : les comptes utilisateurs (passwd + shadow) et les groupes (group). Les deux entrent dans le périmètre.

Décision

1. Source de vérité binaire sous /etc/air/

La base comptes canonique d’Air est un ensemble d’artefacts binaires :

FichierContenuPermissions
/etc/air/passwd.aircfgcomptes (name, uid, gid, gecos, dir, shell)0644
/etc/air/group.aircfggroupes (name, gid, membres)0644
/etc/air/shadow.aircfgcredentials (name, hash Argon2id PHC, champs de vieillissement)0600

Chaque artefact suit l’enveloppe éprouvée ([ADR-096]/[ADR-099]) : magie AIRCFGv1 (8 o) + checksum FNV-1a-64 (8 o LE) + message Cap’n Proto. Les schémas .capnp (passwd / group / shadow) et leur code généré commité vivent dans une nouvelle crate schéma couche 1 air-account-schema — miroir exact d’air-sshd-schema (codegen régénérable via regenerate.sh avec le capnp C++ pinné [ADR-025] ; le build ne consomme que le crate capnp pur-Rust).

2. air-account reste le médiateur unique, à API gelée

air-account ne change aucune signature publique ni aucune struct exposée (PasswdEntry, GroupEntry, ShadowEntry, ShadowHash) : seule la source qu’il lit change. Résolution en deux temps :

  1. binaire /etc/air/*.aircfg — canonique, lu en priorité (parseur binaire fuzzé, panic-free) ;
  2. texte /etc/{passwd,group,shadow}fallback transitoire de coexistence (systèmes non encore migrés), conservant le codec texte existant.

Conséquence — compatibilité des deux toits par construction. Parce que libair_c et la std portée passent tous deux par l’API d’air-account, et que cette API est inchangée, ils restent compatibles sans recâblage : ils bénéficient de la source binaire de façon transparente. La preuve est leurs tests existants qui restent verts, complétée par des tests de conformité dédiés (§Incréments A.6). C’est le descellement additif couche-1-v2.3 → v2.4.

3. Projection texte /etc pour le monde C ([ADR-041])

Le binaire étant canonique, /etc/passwd, /etc/group, /etc/shadow texte deviennent des projections générées (backend d’émission [ADR-041] §1) : lecture seule sélective, air-config/le CLI seul écrivain, réécriture par swap atomique (air-filesystem::write_atomic). Elles existent uniquement pour les programmes C liés à glibc/musl qui lisent /etc directement au runtime. Import inverse au bootstrap (/etc texte existant → binaire Air) pour migrer un système en place ([ADR-041] §3 « bootstrap bidirectionnel »).

4. Un outil d’administration en ligne de commande (couche 2)

Un exécutable unique administre comptes et groupes — verbes git-style, cohérents avec air-ssh config ([ADR-099]) :

air-account user  add|mod|del  <name> [--uid …] [--gid …] [--home …] [--shell …] [--gecos …]
air-account group add|mod|del  <name> [--gid …] [--members a,b,c]
air-account passwd <name>                      # (re)définit le hash Argon2id (air-crypto)
air-account export|import [--json]             # interchange (secrets élidés par défaut)

Règles :

  • Validation avant écriture (Principe 4) : toute mutation valide le schéma avant d’écrire ; air-config/le CLI seul écrivain ; swap atomique.
  • Mots de passe : hachés Argon2id (format PHC, air-crypto::AirArgon2) ; le clair n’est jamais stocké ; tampons zeroïsés (ShadowHash l’est déjà).
  • Gate mot de passe administrateur ([ADR-073] §2) : les mutations exigent la ressaisie du mot de passe admin — désormais réalisable, puisque shadow binaire fournit le credential de référence. (Le mécanisme exact de vérification admin est précisé dans l’incrément A.5.)
  • JSON = interchange transitoire ([ADR-099] §5, ratifié) : jamais la base-au-repos ; l’import re-valide et ré-encode vers le binaire canonique ; secrets élidés des exports par défaut.

5. Sûreté

  • Parseur binaire de la base = entrée non fiablefuzzé (cargo-fuzz, règle Principe 1 / CLAUDE.md couches 0/1), invariant panic-free, zéro unwrap/indexation paniquante.
  • shadow : artefact 0600, hash zeroïsé (ShadowHash), getspnam_r rend EACCES en non-privilégié (comportement existant préservé, [ADR-067]).
  • Déterminisme de l’artefact ([ADR-025]) : encodage stable, ordre des entrées canonique (tri par uid/gid), aucun timestamp/adresse dans la sortie.

Incréments (chaque incrément = 1 PR verte ; fuzz sur tout parseur ; deux toits verts)

Inc.ContenuPreuve
A.0Cet ADR (design + plan).ratifié
A.1Crate air-account-schema : schémas .capnp passwd/group/shadow + codegen commité + enveloppe AIRCFGv1.round-trip encode/decode ; reproductibilité byte-identique
A.2Lecteur binaire dans air-account (descellement v2.4) : /etc/air/*.aircfg prioritaire, fallback texte ; parseur fuzzé.lookup_*/shadow_by_name résolvent depuis le binaire ; fuzz 1M/0
A.3Écrivain binaire + projection texte /etc ([ADR-041]) + import inverse bootstrap.swap atomique ; /etc/passwd généré relu par glibc ; import /etc→binaire
A.4CLI air-account — utilisateurs : user add/mod/del + passwd (Argon2id).création d’un compte → visible via getpwnam et air-account
A.5CLI air-account — groupes + gate mot de passe admin ([ADR-073]).group add/mod/del ; mutation refusée sans mot de passe admin
A.6Preuve compatibilité deux toits : conformité libair_c (getpwnam/getgrnam/getspnam/initgroups) et std portée contre la source binaire ; re-sceau couche-1-v2.4.suites libc + std vertes sur base binaire ; tag

(Ordonnancement : A.1→A.2→A.3 posent la base ; A.4→A.5 l’outil ; A.6 verrouille la compatibilité et re-scelle. Le blocage capnp d’[ADR-096]/[ADR-099] est levé : l’outil est installé et vérifié reproductible.)

Conséquences

La base de comptes devient binaire et possédée : les codecs texte de /etc/passwd cessent d’être la source de vérité d’Air, tout en restant lisibles pour l’extérieur.

Cela rend l’administration dépendante d’un outil dédié — le format n’est plus éditable au vi, ce qui est le but recherché.

Alternatives rejetées

  • Garder /etc/passwd/shadow/group texte comme canonique — viole [ADR-073] (doctrine binaire) ; c’est précisément la surface d’édition triviale à supprimer. Le texte /etc reste, mais en projection générée (lecture C), pas en source.
  • Un lecteur/écrivain binaire hors d’air-account (nouvelle crate consommée directement par les toits) — casserait le médiateur unique : libair_c et la std divergeraient de la logique compte, et la compatibilité ne serait plus « par construction ». Rejeté : air-account reste le point unique ([ADR-067]).
  • Plusieurs exécutables (air-useradd/air-groupadd/air-passwd, façon Unix) — la demande est un outil unique ; un CLI à verbes (user/group/passwd) est cohérent avec air-ssh config ([ADR-099]/[ADR-095]) et réduit la surface.
  • Base par-utilisateur (XDG)passwd/group/shadow sont système ; elles vivent sous /etc/air, air-config seul écrivain ([ADR-041]). (La conf cliente air-ssh, elle, est XDG — cas distinct.)
  • Rupture d’ABI d’air-account (nouvelles signatures) — inutile : le changement est interne (source lue), l’API suffit telle quelle → descellement additif seulement.

Références

  • ADR-067air-account (couche 1) : comptes/groupes/shadow, bindée par la libc.
  • ADR-073 — doctrine de configuration binaire + CLI + mot de passe admin.
  • ADR-041 — coexistence /etc : source Air canonique, projection générée, seul écrivain.
  • ADR-040 / ADR-033 — artefact binaire Cap’n Proto, modèle de configuration.
  • ADR-096 — schéma bespoke (enveloppe AIRCFGv1 + checksum), précédent.
  • ADR-099 — sous-système de configuration air-ssh (CLI config, JSON interchange, ratification).
  • ADR-025 — builds reproductibles (pin capnp, déterminisme d’artefact).
  • Principe 1 d’ingénierie / CLAUDE.md (couches 0/1) — coverage 100 % + fuzzing systématique des parseurs d’entrée externe (cargo-fuzz).