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
- 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.
- 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.
- 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.
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 --checkaveugle)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.kdbxdevient 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 desecrets-hygiene.mdqu'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)
.secrets/master.env, un par machine, 26 clés sur ai-01.render_envs.pyle propage vers les.envde chaque serviceMyIA-Keys.kdbxsur le volume partagé RooSync, 10 entrées. Passphrase dans le gestionnaire d'identifiants Windows (DPAPI), par machinescripts/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éeTrois familles de consommateurs, et ce que « pas en clair » veut dire pour chacune
gh, sudo). Ils lisent le coffre au moment de l'appel :agent_keyring.py get, plus unagent_keyring.py run -- <commande>à écrire, qui injecte les variables dans l'environnement du seul processus enfant. Aucun fichier.docker compose.composeinterpole${VAR}depuis l'environnement du processus qui lanceup. Doncagent_keyring.py run -- docker compose up -dremplace le.envrendu. Limite à écrire, pas à cacher : la valeur vit ensuite dans la configuration du conteneur, etdocker inspectla 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 montagetmpfsou des secrets Docker, cas par cas.stdindu 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) litGH_RUNNERS_ADMIN_TOKENdirectement dans/mnt/d/CoursIA/.secrets/master.envà chaque démarrage. C'est le premier consommateur à basculer avant de retirermaster.envsur 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 :
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
agent_keyring.py doctorrend 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ù sonmaster.envdisparaîtset(écrivain unique, garde de conflit) etrun -- <cmd>setmaster.envdans le coffre, clé par clérender_envs.py --checkcompare aussi le coffre àmaster.envexit 0sur les 7 machines. Ce contrôle couvre aussi l'angle mort de #14373master.envet des.envrendus, machine par machine, seulement après la phase 5 sur cette machine.envde services ne trouve aucune valeur des clés deSECRET_KEYSau repossecrets-hygiene.md(règle 1, section centralisation)Premier secret à migrer : les identifiants sudo WSL (
jesse), aujourd'hui dans lemaster.envd'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