Files
flowdeck/docs/coffre-reference-flowdeck.md
T
bruno 62b536c865
FlowDeck CI / lint (push) Failing after 2s
FlowDeck CI / test (push) Successful in 11m19s
FlowDeck CI / docker (push) Failing after 2m5s
FlowDeck CI / lint (pull_request) Failing after 0s
FlowDeck CI / test (pull_request) Failing after 0s
FlowDeck CI / docker (pull_request) Skipped
coffre ObsiGate Phase 4 (issue #22) : docs/coffre-reference-flowdeck.md
2026-10-27 23:48:35 -04:00

6.7 KiB
Raw Blame History

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_encrypted reste 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/distribuer d'ObsiGate avec le jeton consommateur Flowdeck et un contexte flowdeck_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_id dé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 :

  1. (optionnel) rattacher d'abord le connecteur à son workspace (PATCH /api/agent/connectors/{id} avec workspace_id) ;
  2. puis basculer vers la référence (PATCH … avec coffre_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 ;
  3. 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/connectors accepte workspace_id et coffre_reference (référence sans workspace → 400).
  • PATCH /api/agent/connectors/{id} accepte workspace_id (premier rattachement seulement) et coffre_reference ("" = effacer la référence).
  • POST /api/agent/connectors/probe accepte workspace_id.
  • Réponses : workspace_id, coffre_reference, secret_source (coffre | chiffre_local | aucun) — jamais de valeur.
  • GET /api/agent/connectors liste 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_id depuis l'agent : AgentEngine.workspace_id → connector_fetch (outil LLM) et McpTool.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_reference via l'API seulement).