Phase 5 (issue #23) : deploiement et mise en service du coffre Secrets
This commit is contained in:
@@ -0,0 +1,390 @@
|
|||||||
|
# Coffre Secrets ObsiGate — Déploiement et mise en service (Phase 5, issue #23)
|
||||||
|
|
||||||
|
Document de déploiement du coffre Secrets, issu des Phases 1a/1b
|
||||||
|
(issues #17, #18), 2 (issue #20), 3 (issue #21, côté agent-commun)
|
||||||
|
et 4 (issue #22, côté TikTak, Flowdeck, Kanbante).
|
||||||
|
|
||||||
|
**Règles de gouvernance (non négociables) :** travail en branche +
|
||||||
|
pull request, jamais sur `main`, jamais de merge, de release, de tag,
|
||||||
|
de fermeture d'issue ni de changement de label sans le go explicite
|
||||||
|
de Bruno. Ce document ne décrit aucune valeur réelle : les exemples
|
||||||
|
utilisent des valeurs fictives (`TEST-VALUE-XXXX`) et aucune KEK
|
||||||
|
n'est jamais écrite ici, ni dans un journal, ni dans une note, ni
|
||||||
|
dans le dépôt.
|
||||||
|
|
||||||
|
Documents compagnons (même branche de référence, Phase 2) :
|
||||||
|
|
||||||
|
- `docs/COFFRE-contrat-distribution.md` — contrat
|
||||||
|
`POST /api/coffres/distribuer` pour Spark, TikTak, Flowdeck,
|
||||||
|
Kanbante (authentification par consommateur, contextes, codes
|
||||||
|
d'erreur stables).
|
||||||
|
- `docs/COFFRE-sauvegarde-restauration.md` — sauvegarde
|
||||||
|
(`data/coffres.json` + `data/coffres_valeurs.json`, 0600) et
|
||||||
|
restauration sur instance vierge, perte de KEK.
|
||||||
|
|
||||||
|
## 1. Prérequis et variables d'environnement
|
||||||
|
|
||||||
|
### 1.1 Dépendance
|
||||||
|
|
||||||
|
- `cryptography>=44.0` dans `backend/requirements.txt` (dépendance
|
||||||
|
Phase 1a). Sans elle, les opérations de chiffrement répondent
|
||||||
|
503 `crypto_indisponible` : c'est un mode dégradé explicite, pas
|
||||||
|
un repli en clair. Il n'y a jamais de stockage ni de distribution
|
||||||
|
de valeur en clair.
|
||||||
|
- Chiffrement par enveloppe AES-256-GCM : KEK (256 bits) → DEK par
|
||||||
|
coffre → valeur du secret. Un tag GCM invalide refuse le
|
||||||
|
déchiffrement (testé : `tests/test_coffre_secrets.py`,
|
||||||
|
`test_tag_invalide_refuse`).
|
||||||
|
|
||||||
|
### 1.2 Variables
|
||||||
|
|
||||||
|
| Variable | Rôle | Défaut |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `OBSIGATE_SECRETS_ENABLED` | Activation du coffre. Tant qu'elle n'est pas à `true`, **toutes** les routes coffre répondent 503 `secrets_desactives`. | désactivé |
|
||||||
|
| `OBSIGATE_KEK` | KEK, base64 de 32 octets (ou hex 64 caractères). **Prioritaire** sur le fichier si les deux sont définis. | absente |
|
||||||
|
| `OBSIGATE_KEK_FILE` | Chemin d'un fichier contenant la KEK, **en 0600, hors dépôt et hors arborescence des vaults**. Un fichier trop permissif (droits groupe/autre) est refusé explicitement (`KEKInvalideError`). | absent |
|
||||||
|
| `OBSIGATE_DATA_DIR` | Répertoire des données (`data/` par défaut) : `coffres.json`, `coffres_valeurs.json` et l'historique `api_keys.json`. | `data` |
|
||||||
|
|
||||||
|
La KEK est générée une seule fois (admin, coffres activés) via
|
||||||
|
`POST /api/coffres/admin/generer-kek` ; ObsiGate **ne la stocke
|
||||||
|
jamais**. Au moment de sa génération : la copier immédiatement
|
||||||
|
dans un gestionnaire de mots de passe ou sur un support hors ligne,
|
||||||
|
hors du serveur ; vérifier la copie en la relisant depuis le
|
||||||
|
gestionnaire, jamais depuis l'écran qui ne l'affichera plus.
|
||||||
|
Ne jamais la coller dans le dépôt, dans une note ObsiGate, dans le
|
||||||
|
même dossier que `data/`, ni dans un journal.
|
||||||
|
|
||||||
|
**Recommandation de déploiement : préférer `OBSIGATE_KEK_FILE`**
|
||||||
|
(fichier 0600) à `OBSIGATE_KEK` en variable d'environnement, pour
|
||||||
|
éviter que la KEK apparaisse dans un historique de processus ou un
|
||||||
|
dump d'environnement. Si les deux sont définis, `OBSIGATE_KEK`
|
||||||
|
l'emporte — vérifier qu'aucune ancienne valeur ne traîne dans
|
||||||
|
l'environnement au moment de basculer sur le fichier.
|
||||||
|
|
||||||
|
### 1.3 Ce que la sauvegarde contient (rappel)
|
||||||
|
|
||||||
|
Deux fichiers seulement portent le coffre, tous deux en 0600 :
|
||||||
|
|
||||||
|
- `data/coffres.json` : métadonnées des coffres, DEK chiffrées,
|
||||||
|
consommateurs (hashes SHA-256 des jetons seulement, jamais le
|
||||||
|
jeton en clair), octrois, métadonnées des secrets ;
|
||||||
|
- `data/coffres_valeurs.json` : versions chiffrées des secrets.
|
||||||
|
|
||||||
|
Copiés seuls, ils ne donnent accès à aucune valeur. La KEK ne fait
|
||||||
|
pas partie de la sauvegarde : elle est copiée à part, hors serveur.
|
||||||
|
Sauvegarder les deux fichiers et la KEK au même endroit annulerait
|
||||||
|
la protection du chiffrement.
|
||||||
|
|
||||||
|
## 2. Décisions ouvertes de la spec — tranchées par défaut
|
||||||
|
|
||||||
|
Les décisions ouvertes de la spec (§15, §16), rappelées dans
|
||||||
|
l'issue #23, sont confirmées comme tranchées selon les défauts
|
||||||
|
recommandés, en attendant une décision contraire explicite de
|
||||||
|
Bruno. Aucune d'elles ne nécessite de changement de code pour la
|
||||||
|
mise en service décrite ci-dessous :
|
||||||
|
|
||||||
|
| Décision | Choix confirmé par défaut |
|
||||||
|
| --- | --- |
|
||||||
|
| Glyphe du nœud Secrets | Cadenas U+1F510 (🔐), distinct de la maison du Home et du diamant du Partage. Le nœud est un nœud virtuel sous le Home de l'utilisateur courant seulement (voir §4). |
|
||||||
|
| Stockage de la KEK | Par fichier 0600, hors dépôt (`OBSIGATE_KEK_FILE`) ; `OBSIGATE_KEK` reste supportée comme solution de secours. |
|
||||||
|
| Expiration des octrois | 90 jours par défaut à la création, renouvelable (création d'un nouvel octroi). Un octroi expiré refuse la distribution (`octroi_expire`, 403) ; un octroi révoqué refuse dès l'appel suivant (`octroi_revoque`, 403). |
|
||||||
|
| Copie manuelle | Presse-papiers seul, sans affichage de la valeur. Patron supporté : consommateur temporaire créé par un admin puis révoqué immédiatement, auditée comme distribution de contexte `copie_manuelle`. **Aucun endpoint dédié de copie par session n'est ajouté** : il contredirait la règle « distribution réservée aux consommateurs » et exigerait une ré-authentification qui n'existe pas encore — décision volontaire, à trancher avec Bruno avant toute implémentation. |
|
||||||
|
| Coffres partagés | Phase 2 ultérieure. En mise en service : coffres personnels d'abord, plus le coffre d'instance « ObsiGate interne » (slug `obsigate-interne`, admin seulement) issu de l'import. |
|
||||||
|
|
||||||
|
## 3. Mise en service pas à pas
|
||||||
|
|
||||||
|
**Ordre impératif.** Chaque étape est validée avant de passer à la
|
||||||
|
suivante. En particulier : **les lecteurs internes sont branchés
|
||||||
|
sur le coffre AVANT toute purge du clair** (étape 5 avant étape 7),
|
||||||
|
et la purge n'a lieu qu'après validation complète (étape 6).
|
||||||
|
|
||||||
|
### Étape 0 — Préparer, coffre encore désactivé
|
||||||
|
|
||||||
|
1. Déployer le code coffre (Phases 1–2) sur l'instance, **sans
|
||||||
|
activer le flag** : `OBSIGATE_SECRETS_ENABLED` reste absent ou à
|
||||||
|
`false`. Vérifier que toutes les routes `/api/coffres/*`
|
||||||
|
répondent 503.
|
||||||
|
2. Sauvegarder l'état actuel : copie de `data/` (dont
|
||||||
|
`api_keys.json`) et de la configuration, hors serveur.
|
||||||
|
3. Générer la KEK (voir §1.2) et la copier hors serveur. Ne pas
|
||||||
|
encore la référencer dans la configuration de l'instance si
|
||||||
|
l'étape 1 n'est pas terminée — une KEK présente sans flag actif
|
||||||
|
ne distribue rien, mais sa présence doit rester traçable.
|
||||||
|
|
||||||
|
### Étape 1 — Créer les consommateurs et les octrois
|
||||||
|
|
||||||
|
Avant d'activer le flag, préparer les consommateurs nommés, un par
|
||||||
|
application cliente (convention de nommage, non imposée par le
|
||||||
|
code — l'autorisation porte sur le jeton, pas sur le nom) :
|
||||||
|
|
||||||
|
| Consommateur | Usage | Contexte de distribution |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `spark` | Agent Spark (agent-commun, Phase 3) | `contexte_type = "mission"`, `contexte_id` = id de mission (forme imbriquée `contexte: {type, id}` acceptée) |
|
||||||
|
| `tiktak` | Synchronisation TikTak (Phase 4) | `contexte_type = "tiktak_sync"`, `contexte_id` = id utilisateur |
|
||||||
|
| `flowdeck` | Connecteurs Flowdeck (Phase 4) | Contexte workspace, vérifié côté Flowdeck (voir §5) |
|
||||||
|
| `kanbante` | Webhooks Kanbante (Phase 4) | Contexte webhook, distribution à chaque appel puis oubli |
|
||||||
|
|
||||||
|
Création en une seule opération avec les octrois :
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/coffres/consommateurs/avec-octrois
|
||||||
|
{"nom": "tiktak", "octrois": [{"coffre_slug": "...", "secret_nom": "..."}]}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `secret_nom` absent = tout le coffre (octroi coffre-wide).
|
||||||
|
- Le jeton (`csk_…`) n'est rendu **qu'une seule fois**, à la
|
||||||
|
création. Le noter immédiatement dans le coffre de
|
||||||
|
configuration de l'application cliente concernée, jamais dans
|
||||||
|
une note ObsiGate ni dans le dépôt. Il n'est jamais relisté :
|
||||||
|
`GET /api/coffres/consommateurs` ne renvoie que `id`, `nom`,
|
||||||
|
`jeton_prefixe`, `statut`, `cree_par`, `cree_le`.
|
||||||
|
- La révocation est immédiate : `DELETE
|
||||||
|
/api/coffres/consommateurs/{id}` révoque toutes les
|
||||||
|
distributions de ce consommateur, sans toucher aux autres.
|
||||||
|
Une mission Spark déjà démarrée conserve son environnement
|
||||||
|
jusqu'à sa fin ; la mission suivante repasse en `acces_requis`.
|
||||||
|
|
||||||
|
À ce stade, les consommateurs et octrois peuvent être créés alors
|
||||||
|
que les secrets n'existent pas encore : les octrois pointent vers
|
||||||
|
des coffres/secrets à créer aux étapes suivantes. Vérifier la
|
||||||
|
liste des consommateurs et des octrois avant d'activer le flag.
|
||||||
|
|
||||||
|
### Étape 2 — Activer le flag
|
||||||
|
|
||||||
|
1. Définir `OBSIGATE_KEK_FILE` (fichier 0600, hors dépôt) puis
|
||||||
|
`OBSIGATE_SECRETS_ENABLED=true`.
|
||||||
|
2. Redémarrer l'instance.
|
||||||
|
3. Vérifier : la vue Secrets s'ouvre ; `GET /api/coffres` répond
|
||||||
|
200 (liste vide ou coffres existants) ; sans KEK valide, les
|
||||||
|
routes répondent 503 `kek_absente` — dans ce cas, corriger la
|
||||||
|
KEK avant toute création de secret.
|
||||||
|
|
||||||
|
### Étape 3 — Importer l'existant (admin seulement)
|
||||||
|
|
||||||
|
Bouton « Importer l'existant » de la vue Secrets (admin
|
||||||
|
seulement), en deux temps séparés :
|
||||||
|
|
||||||
|
1. **Aperçu** : `GET /api/coffres/migration/api-keys` liste les
|
||||||
|
candidats de `data/api_keys.json` (clés IA et clés d'outils).
|
||||||
|
2. **Import** : `POST /api/coffres/migration/api-keys/import`
|
||||||
|
copie les entrées avec valeur vers le coffre d'instance
|
||||||
|
« ObsiGate interne » (slug `obsigate-interne`), chiffrées.
|
||||||
|
Un secret déjà présent n'est **jamais écrasé**.
|
||||||
|
`api_keys.json` n'est **pas modifié** par l'import.
|
||||||
|
|
||||||
|
Vérifier après import : les secrets importés apparaissent dans la
|
||||||
|
vue Secrets avec `has_value = true`, et aucune valeur n'est
|
||||||
|
visible (métadonnées seules : nom, type, version, dates, octrois
|
||||||
|
actifs — jamais de valeur, jamais de texte chiffré, jamais de
|
||||||
|
nonce).
|
||||||
|
|
||||||
|
### Étape 4 — Brancher les lecteurs internes AVANT toute purge
|
||||||
|
|
||||||
|
**Point de vigilance principal de la mise en service.** Les
|
||||||
|
lecteurs actuels d'ObsiGate (`backend/ai.py`,
|
||||||
|
`backend/tools/secrets.py`) lisent encore `data/api_keys.json`
|
||||||
|
directement ; ils ne sont pas encore branchés sur le coffre
|
||||||
|
(reste à faire explicite des Phases 2–5). Tant que ce branchement
|
||||||
|
n'est pas livré et validé, **purger le clair priverait l'IA et les
|
||||||
|
outils de leurs clés**.
|
||||||
|
|
||||||
|
Ordre à respecter :
|
||||||
|
|
||||||
|
1. Ne pas purger.
|
||||||
|
2. Livrer (en branche + PR, jamais mergé sans go) le branchement
|
||||||
|
des lecteurs internes sur une distribution depuis le coffre
|
||||||
|
d'instance, puis le valider sur l'instance : l'IA et les outils
|
||||||
|
fonctionnent avec `api_keys.json` rendu temporairement
|
||||||
|
inaccessible en lecture (test de coupure contrôlée), ou à
|
||||||
|
défaut avec un secret de test distribué depuis le coffre.
|
||||||
|
3. Seulement après cette validation, passer à l'étape 7.
|
||||||
|
|
||||||
|
Si Bruno accepte une interruption temporaire de l'IA/outils, le
|
||||||
|
documenter explicitement avant la purge ; sinon, la purge attend
|
||||||
|
le branchement.
|
||||||
|
|
||||||
|
### Étape 5 — Brancher les applications clientes
|
||||||
|
|
||||||
|
Pour chaque application (branches dédiées, jamais mergées sans
|
||||||
|
go), appliquer le patron commun **distribuer → utiliser →
|
||||||
|
oublier** : le serveur distribue à chaque opération, utilise la
|
||||||
|
valeur en mémoire, puis l'oublie ; aucune valeur en cache, aucune
|
||||||
|
persistance. Si ObsiGate est indisponible, l'opération échoue
|
||||||
|
proprement, sans repli sur une ancienne valeur.
|
||||||
|
|
||||||
|
- **Spark (agent-commun, Phase 3)** : identités référencées
|
||||||
|
`coffre:<slug>/<nom>` (slug 3–64, nom 3–128, minuscules /
|
||||||
|
chiffres / `-` / `_`), sans colonne valeur ; le LLM ne voit que
|
||||||
|
la référence et un accès oui/non. Sans octroi, la mission passe
|
||||||
|
en `acces_requis` en nommant l'accès manquant ; avec octroi et
|
||||||
|
mission approuvée (enveloppe budgétaire), distribution puis
|
||||||
|
injection en variable d'environnement de l'exécuteur, jamais
|
||||||
|
dans le prompt, jamais dans les étapes d'activité. Fin de
|
||||||
|
mission = environnement détruit.
|
||||||
|
- **TikTak (Phase 4)** : référence seule stockée ; distribution
|
||||||
|
serveur à chaque synchro ; masquage côté Android (ni référence
|
||||||
|
complète ni valeur dans les réponses vues par une clé `tck_`).
|
||||||
|
Les configurations existantes sont marquées `a_resaisir = 1`
|
||||||
|
(migration 019 : colonne en clair supprimée, référence vide) :
|
||||||
|
il n'y a **pas de conversion automatique** — chaque
|
||||||
|
configuration doit être re-saisie par référence.
|
||||||
|
- **Flowdeck (Phase 4)** : `workspace_id` + référence sur les
|
||||||
|
connecteurs ; distribution en mémoire ; isolation A vs B
|
||||||
|
testée (un workspace ne distribue pas avec l'octroi d'un autre).
|
||||||
|
Les connecteurs natifs Gitea/GitHub et les clés LLM ne sont pas
|
||||||
|
basculés ; pas de sélecteur UI (API seulement).
|
||||||
|
- **Kanbante (Phase 4)** : secret webhook migré en référence,
|
||||||
|
distribuée à chaque webhook puis oubliée ; client générique
|
||||||
|
réutilisable. Les jetons forge chiffrés Fernet ne sont **pas**
|
||||||
|
migrés (distribution par appel requise, incompatible avec le
|
||||||
|
cache `ForgeRegistry`) ; les secrets webhook historiques en
|
||||||
|
clair restent acceptés par compatibilité, sans migration
|
||||||
|
automatique.
|
||||||
|
|
||||||
|
### Étape 6 — Valider (avant purge)
|
||||||
|
|
||||||
|
Exécuter la checklist de validation de l'instance réelle (§4).
|
||||||
|
Tant qu'un point échoue, ne pas purger et ne pas considérer la
|
||||||
|
mise en service comme terminée.
|
||||||
|
|
||||||
|
### Étape 7 — Purger, seulement après validation
|
||||||
|
|
||||||
|
`POST /api/coffres/migration/api-keys/purger`, **après
|
||||||
|
confirmation explicite** (`confirmer = true`), et uniquement pour
|
||||||
|
les noms migrés. Sans confirmation, rien n'est supprimé — jamais
|
||||||
|
automatiquement.
|
||||||
|
|
||||||
|
Après purge, vérifier :
|
||||||
|
|
||||||
|
- `api_keys.json` ne contient plus les noms purgés ;
|
||||||
|
- l'IA et les outils fonctionnent toujours (lecteurs branchés,
|
||||||
|
étape 4) ;
|
||||||
|
- une distribution de test via un consommateur existant aboutit
|
||||||
|
(succès seul vérifié, valeur ni affichée ni consignée).
|
||||||
|
|
||||||
|
### Étape 8 — Sauvegarder et tester la restauration
|
||||||
|
|
||||||
|
1. Sauvegarder `data/coffres.json` + `data/coffres_valeurs.json`
|
||||||
|
(0600) par la sauvegarde habituelle du serveur, et la KEK à
|
||||||
|
part, hors serveur (§1.3).
|
||||||
|
2. Rejouer la restauration sur une instance vierge (procédure
|
||||||
|
`docs/COFFRE-sauvegarde-restauration.md` §3 : arrêt de la
|
||||||
|
cible, copie des deux fichiers en 0600, même KEK, redémarrage,
|
||||||
|
vérification `has_value = true` + distribution de test +
|
||||||
|
`distribution_ok` dans le journal). La suite automatisée
|
||||||
|
(`tests/test_coffre_secrets.py`, classe
|
||||||
|
`TestRestaurationVierge`) simule cette restauration ; elle ne
|
||||||
|
remplace pas un essai sur instance réelle.
|
||||||
|
3. En cas de perte de KEK : perte d'accès assumée, par
|
||||||
|
construction — aucun moyen de récupérer les valeurs. Recréer
|
||||||
|
les coffres avec une nouvelle KEK, régénérer les valeurs côté
|
||||||
|
fournisseur, révoquer les anciennes côté fournisseur, recréer
|
||||||
|
consommateurs et octrois. Ne jamais tenter de « réparer » les
|
||||||
|
anciens fichiers avec une nouvelle KEK (le déchiffrement
|
||||||
|
échouera sur tag GCM invalide, ce qui est attendu).
|
||||||
|
|
||||||
|
## 4. Checklist de validation par Bruno sur instance réelle
|
||||||
|
|
||||||
|
Validation exigée par l'issue #23 avant tout usage réel. Chaque
|
||||||
|
point est validé sur capture, comme pour les autres livraisons :
|
||||||
|
|
||||||
|
- [ ] **Sidebar** : le nœud « Secrets » (cadenas U+1F510)
|
||||||
|
apparaît sous le Home de l'utilisateur courant seulement, et
|
||||||
|
pas sous les autres vaults ; cliquer dessus ouvre la vue
|
||||||
|
Secrets sans naviguer dans les fichiers.
|
||||||
|
- [ ] **Secret de test** : création d'un secret de test
|
||||||
|
(valeur fictive, ex. `TEST-VALUE-XXXX`) dans un coffre
|
||||||
|
personnel, puis lecture : métadonnées seules, aucune valeur
|
||||||
|
affichée, aucun texte chiffré, aucun nonce.
|
||||||
|
- [ ] **Journal d'audit** : après une distribution de test, le
|
||||||
|
journal contient `distribution_ok` pour la référence, et un
|
||||||
|
`grep` du journal sur la valeur de test ne renvoie **rien**
|
||||||
|
(ni valeur, ni texte chiffré, ni nonce, ni jeton).
|
||||||
|
- [ ] **Autorisations** : un admin ne voit pas le coffre
|
||||||
|
personnel d'autrui ; une ACL `*` sur un vault ne donne aucun
|
||||||
|
droit sur un coffre ; une session utilisateur, même admin, ne
|
||||||
|
distribue pas (401 `consommateur_invalide`).
|
||||||
|
- [ ] **Distribution** : contexte manquant refusé (400
|
||||||
|
`contexte_manquant`) ; octroi révoqué refusé dès l'appel
|
||||||
|
suivant (403 `octroi_revoque`) ; octroi expiré refusé (403
|
||||||
|
`octroi_expire`) ; secret expiré refusé (410 `secret_expire`).
|
||||||
|
- [ ] **Import / purge** : l'import ne modifie pas
|
||||||
|
`api_keys.json` et n'écrase pas un secret existant ; la purge
|
||||||
|
sans confirmation explicite ne supprime rien.
|
||||||
|
- [ ] **Restauration** : sauvegarde + KEK externe restaurées sur
|
||||||
|
une instance vierge, distributions fonctionnelles.
|
||||||
|
- [ ] **Intégrations** : mission Spark en `acces_requis` sans
|
||||||
|
octroi, puis injection exécuteur sans trace dans les étapes ;
|
||||||
|
synchro TikTak sans valeur côté Android ; isolation workspace
|
||||||
|
Flowdeck (A ne distribue pas avec l'octroi de B).
|
||||||
|
- [ ] **Modale** : la modale de création de secret n'est plus
|
||||||
|
coupée en haut (`.obsigate-modal` bornée au viewport, corps
|
||||||
|
seul défilant, en-tête et pied toujours visibles).
|
||||||
|
|
||||||
|
## 5. Limites connues à la mise en service
|
||||||
|
|
||||||
|
À lire avant de déclarer la mise en service terminée :
|
||||||
|
|
||||||
|
- **Lecteurs internes non branchés** (`ai.py`,
|
||||||
|
`tools/secrets.py` lisent encore `api_keys.json` en clair) :
|
||||||
|
purger avant de les brancher priverait l'IA/outils de leurs
|
||||||
|
clés (§3, étape 4).
|
||||||
|
- **Endpoint dédié de copie manuelle par session non ajouté**
|
||||||
|
(décision volontaire, §2) : la copie manuelle passe par un
|
||||||
|
consommateur temporaire admin, presse-papiers seul.
|
||||||
|
- **Isolation workspace Flowdeck non contrôlée côté ObsiGate** :
|
||||||
|
`/distribuer` n'autorise aujourd'hui que les octrois de type
|
||||||
|
`consommateur` ; l'isolation workspace repose sur le serveur
|
||||||
|
Flowdeck lui-même (il ne demande que les références de son
|
||||||
|
workspace, avec son propre jeton). Un contrôle serveur strict
|
||||||
|
reste à spécifier si Bruno l'exige.
|
||||||
|
- **Identités agent-commun** : détenteur libre hors coffre
|
||||||
|
conservé (historique) ; révocation Identités non propagée vers
|
||||||
|
ObsiGate (pas d'endpoint) ; exécuteurs CLI synchrones
|
||||||
|
uniquement.
|
||||||
|
- **TikTak** : « sélecteur » = saisie validée, pas de liste
|
||||||
|
déroulante (aucune API de listage côté ObsiGate) ;
|
||||||
|
configurations existantes à re-saisir, pas de conversion
|
||||||
|
automatique.
|
||||||
|
- **Flowdeck** : natifs Gitea/GitHub et clés LLM non basculés ;
|
||||||
|
pas de sélecteur UI (API seulement) ; `workspace_id` pas encore
|
||||||
|
branché depuis AgentEngine.
|
||||||
|
- **Kanbante** : jetons forge chiffrés Fernet non migrés ;
|
||||||
|
pas de migration automatique des secrets webhook historiques.
|
||||||
|
- **Recherche / graphe / index** : par construction, un coffre
|
||||||
|
n'est pas un vault de notes — il ne passe ni par l'indexeur, ni
|
||||||
|
par la recherche, ni par le graphe, et aucun outil MCP de secret
|
||||||
|
n'existe. Aucune métadonnée de secret ne doit apparaître dans
|
||||||
|
ces index (vérification par revue de code, pas de suite
|
||||||
|
automatisée dédiée — voir le bilan Phase 5).
|
||||||
|
|
||||||
|
## 6. Repli (rollback)
|
||||||
|
|
||||||
|
- Désactiver le flag (`OBSIGATE_SECRETS_ENABLED` absent ou
|
||||||
|
`false`) : toutes les routes coffre répondent 503, les fichiers
|
||||||
|
`coffres.json` / `coffres_valeurs.json` restent intacts et
|
||||||
|
re-distribueront dès réactivation avec la même KEK.
|
||||||
|
- Révoquer un consommateur (`DELETE
|
||||||
|
/api/coffres/consommateurs/{id}`) coupe immédiatement ses
|
||||||
|
distributions, sans toucher aux autres consommateurs ni aux
|
||||||
|
secrets.
|
||||||
|
- Ne jamais « réparer » des fichiers chiffrés avec une nouvelle
|
||||||
|
KEK, ne jamais restaurer `coffres.json` sans
|
||||||
|
`coffres_valeurs.json` (et inversement), ne jamais copier la
|
||||||
|
KEK au même endroit que la sauvegarde.
|
||||||
|
|
||||||
|
## 7. Références
|
||||||
|
|
||||||
|
- Spec « Coffre de secrets ObsiGate » §6 (autorisations), §7/§10/§11
|
||||||
|
(API), §13/§14 (sauvegarde/restauration), §15/§16 (tests,
|
||||||
|
déploiement).
|
||||||
|
- Issues ObsiGate : #17 (Phase 1a), #18 (Phase 1b), #19 (Phase 1c
|
||||||
|
UI), #20 (Phase 2), #21 (Phase 3, Spark), #22 (Phase 4, TikTak /
|
||||||
|
Flowdeck / Kanbante), #23 (Phase 5, ce document).
|
||||||
|
- Suites de tests : `tests/test_coffre_secrets.py` (ObsiGate, 35
|
||||||
|
tests), `tests/test_identites_coffre_phase3.py` (agent-commun,
|
||||||
|
16 tests), `tests/test_coffre_reference.py` (TikTak, 17 fonctions
|
||||||
|
/ 26 cas), `tests/test_coffre_reference_flowdeck.py` (Flowdeck,
|
||||||
|
16 tests), `tests/test_coffre_client.py` (Kanbante, 11 fonctions
|
||||||
|
/ 25 cas). Voir le bilan Phase 5 pour la matrice de couverture
|
||||||
|
complète et les manques.
|
||||||
Reference in New Issue
Block a user