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.3 → couche-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/initgroupssont de purs binders qui appellentair_account::lookup_user_by_name/uid,lookup_group_by_name/gid,groups_of_user,shadow_by_name; - la
stdRust portée (target_os="air") :air_accountfait partie de l’ensemble build-std lié par la std suros=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 :
| Fichier | Contenu | Permissions |
|---|---|---|
/etc/air/passwd.aircfg | comptes (name, uid, gid, gecos, dir, shell) | 0644 |
/etc/air/group.aircfg | groupes (name, gid, membres) | 0644 |
/etc/air/shadow.aircfg | credentials (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 :
- binaire
/etc/air/*.aircfg— canonique, lu en priorité (parseur binaire fuzzé, panic-free) ; - 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 (ShadowHashl’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
shadowbinaire 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 fiable → fuzzé (cargo-fuzz,
règle Principe 1 / CLAUDE.md couches 0/1), invariant panic-free, zéro
unwrap/indexation paniquante. shadow: artefact0600, hash zeroïsé (ShadowHash),getspnam_rrendEACCESen 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. | Contenu | Preuve |
|---|---|---|
| A.0 | Cet ADR (design + plan). | ratifié |
| A.1 | Crate air-account-schema : schémas .capnp passwd/group/shadow + codegen commité + enveloppe AIRCFGv1. | round-trip encode/decode ; reproductibilité byte-identique |
| A.2 | Lecteur 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.4 | CLI air-account — utilisateurs : user add/mod/del + passwd (Argon2id). | création d’un compte → visible via getpwnam et air-account |
| A.5 | CLI air-account — groupes + gate mot de passe admin ([ADR-073]). | group add/mod/del ; mutation refusée sans mot de passe admin |
| A.6 | Preuve 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/grouptexte comme canonique — viole [ADR-073] (doctrine binaire) ; c’est précisément la surface d’édition triviale à supprimer. Le texte/etcreste, 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_cet lastddivergeraient de la logique compte, et la compatibilité ne serait plus « par construction ». Rejeté :air-accountreste 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 avecair-ssh config([ADR-099]/[ADR-095]) et réduit la surface. - Base par-utilisateur (XDG) —
passwd/group/shadowsont système ; elles vivent sous/etc/air,air-configseul écrivain ([ADR-041]). (La conf clienteair-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-067 —
air-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(CLIconfig, 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).