Skip to content

Secrets : le coffre MyIA-Keys devient la source unique, plus aucun secret au repos en clair (master.env, .env de services) #17558

Description

@myia-ai-01

Grain: MED/secrets-infra — lane myia-ai-01:CoursIA

See #17418 (jetons GitHub par lane) · See #17425 (organe agent_keyring.py) · See #14373 (render_envs.py --check aveugle)

Mandat

Le user, en session directe le 2026-09-23 : « Il faudra d'ailleurs tout migrer dans le kdbx, non ? plus rien dans des fichiers en clair serait une bonne chose il me semble. »

La cible est donc : le coffre partagé MyIA-Keys.kdbx devient la source unique de tous les secrets de la flotte, et aucun secret ne reste au repos dans un fichier en clair. Ce mandat direct vaut sign-off pour la modification de secrets-hygiene.md qu'il implique, en fin de parcours (règle 1 : « les secrets vivent uniquement dans des fichiers gitignorés »).

État mesuré (ai-01, 2026-09-23)

Élément État
Source actuelle .secrets/master.env, un par machine, 26 clés sur ai-01. render_envs.py le propage vers les .env de chaque service
Coffre MyIA-Keys.kdbx sur le volume partagé RooSync, 10 entrées. Passphrase dans le gestionnaire d'identifiants Windows (DPAPI), par machine
Organe scripts/secrets/agent_keyring.py (PR #17425, non mergée) : doctor, bootstrap, list, show, get, gh-login, verify. Lecture seule : il ne sait pas écrire une entrée
Bootstrap DPAPI 2 machines sur 7 au dernier compte (registre user, Q38)

Trois familles de consommateurs, et ce que « pas en clair » veut dire pour chacune

  1. Scripts et agents (Python, gh, sudo). Ils lisent le coffre au moment de l'appel : agent_keyring.py get, plus un agent_keyring.py run -- <commande> à écrire, qui injecte les variables dans l'environnement du seul processus enfant. Aucun fichier.
  2. Services docker compose. compose interpole ${VAR} depuis l'environnement du processus qui lance up. Donc agent_keyring.py run -- docker compose up -d remplace le .env rendu. Limite à écrire, pas à cacher : la valeur vit ensuite dans la configuration du conteneur, et docker inspect la montre à qui a accès au socket Docker. Cela reste mieux qu'un fichier sur disque, sans être équivalent. Les services qui lisent un fichier de secret (hash ComfyUI-Login, par exemple) passent par un montage tmpfs ou des secrets Docker, cas par cas.
  3. Distributions WSL (runners CI, sudo). WSL n'a pas accès à DPAPI. Le secret passe par l'interop Windows (l'organe côté Windows écrit sur le stdin du processus Linux). Jamais par un fichier sous /mnt/. Consommateur mesuré : le wrapper systemd des runners CI d'ai-01 (/usr/local/bin/coursia-runner-start.sh) lit GH_RUNNERS_ADMIN_TOKEN directement dans /mnt/d/CoursIA/.secrets/master.env à chaque démarrage. C'est le premier consommateur à basculer avant de retirer master.env sur ai-01, sinon le pool CI ne redémarre plus.

La contrainte qui décide du design : un seul écrivain

Le coffre est un fichier binaire unique, synchronisé par le client Drive. Deux machines qui l'écrivent dans la même fenêtre de synchronisation produisent une copie en conflit, et le client ne fusionne pas. Cela ne se voit qu'au moment où une entrée manque. Donc :

  • lecture : toutes les machines ;
  • écriture : un seul écrivain à la fois, le user ou ai-01. La commande agent_keyring.py set à écrire relève l'empreinte du fichier avant d'écrire, la relit après, et refuse si une copie en conflit existe à côté du coffre.

Ordre d'exécution

Phase Geste Condition de sortie mesurable
0 Merger #17425 (organe de lecture) PR mergée
1 Bootstrap DPAPI sur les 7 machines (Q38) agent_keyring.py doctor rend l'empreinte de référence sur 7/7. Préalable dur : une machine sans passphrase perdrait l'accès à tous ses secrets le jour où son master.env disparaît
2 Écrire set (écrivain unique, garde de conflit) et run -- <cmd> tests unitaires, dont un contrôle positif : une copie en conflit fabriquée fait refuser set
3 Importer chaque master.env dans le coffre, clé par clé inventaire sans valeurs par machine. Deux machines qui portent des valeurs différentes pour une même clé sont rapportées, jamais écrasées
4 render_envs.py --check compare aussi le coffre à master.env exit 0 sur les 7 machines. Ce contrôle couvre aussi l'angle mort de #14373
5 Les consommateurs passent à l'injection (familles 1 à 3) services redémarrés, contrôles de santé verts
6 Retrait de master.env et des .env rendus, machine par machine, seulement après la phase 5 sur cette machine fichier absent ; un scan des .env de services ne trouve aucune valeur des clés de SECRET_KEYS au repos
7 Mise à jour de secrets-hygiene.md (règle 1, section centralisation) PR, sign-off porté par le mandat ci-dessus

Premier secret à migrer : les identifiants sudo WSL (jesse), aujourd'hui dans le master.env d'ai-01 seulement. C'est le cas d'usage de la famille 3, et le seul secret que plusieurs machines attendent sans l'avoir.

Ce que ce plan ne fait pas

  • Il ne déplace pas le PDF de secours : sa suppression suit Q38 (7/7), déjà décidée.
  • Il ne change pas le canal de transmission ponctuelle : un DM RooSync privé reste valide pour ce qui n'est pas encore dans le coffre.
  • Il ne réécrit aucun historique git : aucun secret n'y est, et ce plan n'y en met pas.

Activity

  1. jsboige commented on Oct 2, 2026

    @jsboige
    Owner

    [DELIVERED] lane myia-po-2024:CoursIA — PR #18872

    Phase 2 livrée : set (écrivain unique, garde de copie en conflit) et run --env VAR=ENTREE -- <cmd> dans scripts/secrets/agent_keyring.py, plus 31 tests dont le contrôle positif exigé par la condition de sortie de la phase 2.

    Preuves sur la PR : pytest scripts/secrets/tests -q → 273 passed ; copie en conflit fabriquée → refus mesuré en test (coffre inchangé, refus précédant l'ouverture) et en bout-en-bout sur coffre bidon sur disque ; check_assert_secret_egress.py --check → 0 nouveau ; ni set ni run n'impriment la valeur.

    Portée : phases 1 (bootstrap DPAPI, Q38) et 3 à 7 non traitées. set ne crée pas de coffre (coffre absent = rc=2).

    Un [RELEASED] suit immédiatement : le verrou de claim n'a pas à rester sur l'issue pour les phases suivantes.

  2. jsboige commented on Oct 2, 2026

    @jsboige
    Owner

    [RELEASED] lane myia-po-2024:CoursIA — phase 2 livree (PR #18872) ; les phases 1 et 3-7 restent a prendre par qui veut.

  3. added 2 commits that reference this issue on Oct 2, 2026
  4. added a commit that references this issue on Oct 3, 2026
  5. jsboige commented on Oct 3, 2026

    @jsboige
    Owner

    [INFO] candidate-delivered — PR #18872 livre agent_keyring set et run pour #17558 (Secrets : coffre MyIA-Keys devient source unique).

    Mesure first-hand :

    Grain: LIGHT/tooling — lane myia-po-2026:CoursIA-2 — prev: DEEP/lean (c.1374-r38). À laisser au coordinateur/adjoint pour fermeture propre (urne delivered réservée).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions