6.7 KiB
Flowdeck — Connecteurs à référence ObsiGate (Phase 4, issue ObsiGate #22)
Principe
Un connecteur personnel d'un workspace peut pointer vers une référence ObsiGate au lieu d'un jeton :
coffre:<slug>/<nom> ex. coffre:flowdeck/gitea-lecture
- Le champ jeton du connecteur devient le champ référence : aucun
jeton n'est stocké pour ce connecteur (
secret_encryptedreste vide). - L'octroi est toujours lié à un
workspace_id: créer un connecteur à référence sans workspace est refusé (HTTP 400 /ValueError). - À l'appel, le serveur Flowdeck appelle
POST /api/coffres/distribuerd'ObsiGate avec le jeton consommateur Flowdeck et un contexteflowdeck_workspace / <workspace_id>, utilise la valeur en mémoire, puis l'oublie. La valeur n'est jamais persistée, jamais mise en cache, jamais journalisée, jamais renvoyée à une réponse API ni au LLM.
Configuration (serveur Flowdeck)
| Variable | Défaut | Rôle |
|---|---|---|
OBSIGATE_COFFRE_URL |
dérivée de DRACO_SSO_URL |
Base ObsiGate pour /api/coffres/distribuer |
OBSIGATE_CONSUMER_TOKEN |
(vide = distribution désactivée) | Jeton consommateur Flowdeck enregistré dans ObsiGate |
Le jeton consommateur est distinct du jeton MCP de notes et de la session SSO. Il ne figure dans aucun log, rapport ou réponse.
Isolation workspace — ce qui est garanti, et sa limite honnête
Garanti côté Flowdeck, avant tout appel (connectors.resolve_secret) :
- un connecteur à référence ne se résout que pour son propre
workspace_id; - workspace A tentant d'utiliser le connecteur (donc la référence /
l'octroi) du workspace B reçoit un refus explicite
(
CoffreWorkspaceMismatch: « … ne peut pas utiliser ce connecteur (il appartient au workspace …) : octroi d'un autre workspace refusé ») et le distributeur n'est pas appelé ; - un
workspace_iddéjà posé sur un connecteur est immuable : on ne transfère pas un octroi en éditant le connecteur ; - un connecteur à référence testé/appelé sans
workspace_idéchoue proprement, jamais avec la valeur d'un autre workspace.
Limite à connaître : côté ObsiGate, l'octroi est accordé au
consommateur Flowdeck (un jeton par application), pas à un workspace
— le contexte_id transmis sert à la traçabilité, pas à un contrôle
d'accès côté ObsiGate. L'isolation par workspace repose donc sur le
contrôle Flowdeck ci-dessus. Une isolation appliquée côté ObsiGate
exigerait un consommateur par workspace : c'est un reste à faire
décidé côté ObsiGate (enregistrement de consommateurs par workspace),
pas un prérequis pour utiliser ce mécanisme.
Échecs propres, sans repli
| Situation | Comportement Flowdeck |
|---|---|
| ObsiGate injoignable / timeout / HTTP 5xx | CoffreIndisponible — l'appel échoue, aucune valeur en cache n'est utilisée (il n'y en a pas) |
| Octroi révoqué / consommateur révoqué (403) | CoffreAccesRefuse — message « accès révoqué, à ré-accorder dans ObsiGate » |
| Octroi expiré (403) / secret expiré (410) | CoffreAccesRefuse avec le code ObsiGate, sans la valeur |
| Référence mal formée | CoffreReferenceInvalide (format coffre:<slug>/<nom>, slug 3–64, nom 3–128, minuscules, chiffres, -/_, aligné sur ObsiGate) |
Secrets déjà chiffrés : pas de big-bang
Les connecteurs historiques (secret_encrypted Fernet) restent valides
tels quels. La migration est opportuniste, connecteur par
connecteur :
- (optionnel) rattacher d'abord le connecteur à son workspace
(
PATCH /api/agent/connectors/{id}avecworkspace_id) ; - puis basculer vers la référence (
PATCH …aveccoffre_reference: "coffre:<slug>/<nom>") — le jeton local dormant n'est pas effacé automatiquement, il est simplement ignoré tant que la référence est posée ; effacer la référence ("") le réactive ; - vérifier par un probe avec
workspace_id, puis supprimer le jeton local si besoin (rotation côté ObsiGate).
Chemin de migration par connecteur
| Connecteur actuel | État dans cette branche | Chemin |
|---|---|---|
Personnels (custom, discord, telegram, mcp, tiktak, obsigate) |
Implémenté : workspace_id + coffre_reference, résolution en mémoire, isolation testée |
Basculer connecteur par connecteur (ci-dessus) |
| Natifs Gitea / GitHub | Inchangés : jetons globaux d'instance (GITEA_TOKEN, GITHUB_TOKEN en .env) |
Créer un connecteur personnel workspace-scopé à référence vers le même service et l'utiliser à la place du natif, quand le besoin workspace par workspace se présente ; les natifs ne sont pas supprimés (pas de big-bang) |
LLM global (llm_config id=1) et clés par utilisateur (user_llm_keys) |
Inchangés : clés en clair en base (globale / par utilisateur, pas par workspace) | Travail ultérieur dédié : ajouter une colonne référence sur user_llm_keys + résolution par workspace, avec le même patron (distribution en mémoire, workspace obligatoire) ; hors périmètre de cette branche |
OAuth Google / M365 (connector_tokens) |
Inchangés : jetons OAuth chiffrés par (kind, utilisateur) | Hors périmètre : le flux OAuth ne se prête pas à une référence coffre sans repenser le refresh ; à traiter séparément si besoin |
API
POST /api/agent/connectorsaccepteworkspace_idetcoffre_reference(référence sans workspace → 400).PATCH /api/agent/connectors/{id}accepteworkspace_id(premier rattachement seulement) etcoffre_reference(""= effacer la référence).POST /api/agent/connectors/probeaccepteworkspace_id.- Réponses :
workspace_id,coffre_reference,secret_source(coffre|chiffre_local|aucun) — jamais de valeur. GET /api/agent/connectorsliste les connecteurs avec ces champs.
Ce qui reste à faire (validation réelle)
- Valider contre une vraie instance ObsiGate : créer le consommateur
Flowdeck + les octrois, puis un probe réel avec
workspace_id(les tests ici utilisent un faux endpoint de distribution). - Threader
workspace_iddepuis l'agent :AgentEngine.workspace_id→connector_fetch(outil LLM) etMcpTool.call_tool— aujourd'hui ces chemins appellent sans workspace et échouent proprement pour les connecteurs à référence (refus, pas de fuite) ; les connecteurs historiques ne sont pas affectés. - Le cas échéant, consommateur ObsiGate par workspace (voir limite ci-dessus) et bascule des natifs Gitea/GitHub et des clés LLM (tableau ci-dessus).
- Interface : sélecteur de référence dans le formulaire connecteur
(aujourd'hui, champ
coffre_referencevia l'API seulement).