Files
rpa_vision_v3/docs/technique/ARCHITECTURE_CONFIG_AGENT.md
Dom 4f61741420
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
feat: journée 17 avril — tests E2E validés, dashboard fleet+audit, VWB bridge, cleaner C2
Pipeline E2E complet validé :
  Capture VM → streaming → serveur → cleaner → replay → audit trail
  Mode apprentissage supervisé fonctionne (Léa échoue → humain → reprise)

Dashboard :
  - Cleanup 14→10 onglets (RCE supprimée)
  - Fleet : enregistrer/révoquer agents, tokens, ZIP pré-configuré téléchargeable
  - Audit trail MVP (/audit) : filtres, tableau, export CSV, conformité AI Act/RGPD
  - Formulaire Fleet simplifié (nom + email, machine_id auto)

VWB bridge Léa→VWB :
  - Compound décomposés en N steps (saisie + raccourci visibles)
  - Layout serpentin 3 colonnes (plus colonne verticale)
  - Badge OS 🪟/🐧, filtre OS retiré (admin Linux voit Windows)
  - Fix import SQLite readonly

Cleaner intelligent :
  - Descriptions lisibles (UIA/C2) + détection doublons
  - Logique C2 : UIElement identifié = jamais parasite
  - Patterns parasites resserrés
  - Message Léa : "Je n'y arrive pas, montrez-moi comment faire"

Config agent (INC-1 à INC-7) :
  - SERVER_URL + SERVER_BASE unifiés
  - RPA_OLLAMA_HOST séparé
  - allow_redirects=False sur POST
  - Middleware réécriture URL serveur

CI Gitea : fix token + Flask-SocketIO + ruff propre
Fleet endpoints : /agents/enroll|uninstall|fleet + agent_registry SQLite
Backup : script quotidien workflows.db + audit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 17:46:40 +02:00

14 KiB

Architecture de Configuration Agent -- Serveur

Date : 2026-04-13 Auteur : Analyse automatique (Claude) Statut : Diagnostic + recommandation -- PAS de code modifie


1. Schema de la chaine de configuration

  config.txt (fichier plat sur le poste Windows)
       |
       | Lea.bat (for /f "eol=# tokens=1,* delims==" => set)
       v
  Variables d'environnement du process Python
       |
       +---> agent_v1/config.py
       |       SERVER_URL = os.getenv("RPA_SERVER_URL", "http://localhost:5005/api/v1")
       |       STREAMING_ENDPOINT = f"{SERVER_URL}/traces/stream"
       |       API_TOKEN = os.environ.get("RPA_API_TOKEN", "")
       |
       +---> lea_ui/server_client.py
       |       _stream_base = RPA_SERVER_URL (si defini) OU http://{RPA_SERVER_HOST}:{5005}
       |       Utilise _stream_base + "/api/v1/traces/stream/..." (chemin COMPLET en dur)
       |
       +---> agent_v1/main.py
       |       _background_heartbeat_loop : utilise SERVER_URL DIRECTEMENT (pas STREAMING_ENDPOINT)
       |       _replay_poll_loop : passe SERVER_URL a executor.poll_and_execute()
       |
       +---> agent_v1/core/executor.py
               poll_and_execute : construit f"{server_url}/traces/stream/replay/next"
               _server_resolve_target : construit f"{server_url}/traces/stream/replay/resolve_target"
               _observe_screen : construit f"{server_url}/traces/stream/replay/pre_analyze"

Cote serveur (generation du config.txt)

  web_dashboard/app.py
       |
       +---> _RPA_PUBLIC_URL = RPA_PUBLIC_URL ou RPA_SERVER_URL ou "https://lea.labs.laurinebazin.design"
       +---> _build_custom_config(machine_id, user_name, token)
       |       server_url = _RPA_PUBLIC_URL.rstrip("/")
       |       if not server_url.endswith("/api/v1"):
       |           server_url += "/api/v1"            <=== CORRECTIF RECENT (fonctionne)
       |       -> RPA_SERVER_URL={server_url}
       |       -> RPA_SERVER_HOST={host sans schema ni path}
       |
       +---> deploy/lea_package/config.txt (template statique dans le repo)
       +---> deploy/installer/config_template.txt (template pour installeur Inno Setup)

Cote serveur (routes FastAPI, port 5005)

  agent_v0/server_v1/api_stream.py
       |
       +---> TOUTES les routes sous /api/v1/traces/stream/...
       |       /register, /event, /image, /finalize, /replay/next, etc.
       |
       +---> /health (public, a la racine)
       |
       +---> Middleware url_compat_rewrite :
                /traces/stream/... => /api/v1/traces/stream/...

2. Inventaire des incoherences

INC-1 : Deux systemes paralleles de resolution d'URL (CRITIQUE)

Fichiers concernes :

  • agent_v0/agent_v1/config.py (ligne 43-45) : SERVER_URL = URL complete avec /api/v1 ; STREAMING_ENDPOINT = SERVER_URL + "/traces/stream"
  • agent_v0/lea_ui/server_client.py (ligne 77-81) : _stream_base = RPA_SERVER_URL BRUTE (sans garantie de /api/v1) ; utilise ensuite _stream_base + "/api/v1/traces/stream/..." (chemin complet en dur)

Probleme : Si RPA_SERVER_URL=https://lea.labs.laurinebazin.design/api/v1 :

  • config.py produit STREAMING_ENDPOINT = .../api/v1/traces/stream (CORRECT)
  • server_client.py produit _stream_base/api/v1/traces/stream/... = .../api/v1/api/v1/traces/stream/... (DOUBLE /api/v1 !)

Si RPA_SERVER_URL=https://lea.labs.laurinebazin.design :

  • config.py produit STREAMING_ENDPOINT = .../traces/stream (MANQUE /api/v1)
  • server_client.py produit _stream_base/api/v1/traces/stream/... (CORRECT)

Il n'existe aucune valeur de RPA_SERVER_URL qui fasse fonctionner les deux modules simultanement.

INC-2 : _background_heartbeat_loop utilise SERVER_URL au lieu de STREAMING_ENDPOINT

Fichier : agent_v0/agent_v1/main.py (ligne 370)

req.post(f"{SERVER_URL}/traces/stream/image", ...)

SERVER_URL = http://localhost:5005/api/v1 => URL finale = /api/v1/traces/stream/image (CORRECT).

Mais c'est un accident : le heartbeat bypasse STREAMING_ENDPOINT (SERVER_URL + "/traces/stream") et reconstruit son propre chemin. Le meme pattern se retrouve dans executor.py qui recoit server_url (= SERVER_URL) et construit f"{server_url}/traces/stream/replay/next".

Consequence : Deux conventions coexistent :

  1. STREAMING_ENDPOINT + "/register" (streamer.py) -- attend que SERVER_URL contienne /api/v1
  2. SERVER_URL + "/traces/stream/image" (main.py, executor.py) -- attend aussi que SERVER_URL contienne /api/v1

Aujourd'hui les deux marchent parce que SERVER_URL inclut /api/v1. Mais server_client.py utilise une troisieme convention (chemin complet en dur), d'ou l'INC-1.

INC-3 : LeaServerClient.check_connection() appelle /health sur _stream_base

Fichier : agent_v0/lea_ui/server_client.py (ligne 161)

resp = requests.get(f"{self._stream_base}/health", ...)

Si _stream_base = "https://lea.labs.laurinebazin.design/api/v1", l'URL finale est /api/v1/health -- cette route n'existe pas. La route sante est GET /health (racine).

Si _stream_base = "https://lea.labs.laurinebazin.design", l'URL finale est /health -- OK.

Encore un conflit : server_client.py attend une URL sans /api/v1, config.py la fournit avec.

INC-4 : Template deploy/lea_package/config.txt contient de vrais secrets

Fichier : deploy/lea_package/config.txt (ligne 19)

RPA_API_TOKEN=86031addb338e449fccdb1a983f61807aec15d42d482b9c7748ad607dc23caab

Ce fichier est versionne dans Git. Le token reel est en clair dans le repo.

INC-5 : Copie ancienne non maintenue (agent_v0/deploy/windows_client/)

Fichier : agent_v0/deploy/windows_client/config.py (ligne 41)

SERVER_URL = os.getenv("RPA_SERVER_URL", "http://localhost:8000/api/traces/upload")

Cette copie pointe encore sur l'ancien serveur API (port 8000, endpoint /api/traces/upload). Completement obsolete. Idem pour agent_v0/config.py (ligne 41).

INC-6 : RPA_SERVER_HOST sert a deux choses incompatibles

  • server_client.py : utilise RPA_SERVER_HOST comme hostname nu (ex: 192.168.1.40 ou lea.labs.laurinebazin.design) pour construire http://{host}:5005
  • executor.py : utilise RPA_SERVER_HOST pour construire des URLs Ollama (http://{host}:11434/api/chat)
  • main.py (ligne 94-95) : passe RPA_SERVER_HOST comme server_host au LeaServerClient et au ChatWindow

Le config.txt genere par Fleet met RPA_SERVER_HOST=lea.labs.laurinebazin.design. Cela provoque :

  • executor.py tente http://lea.labs.laurinebazin.design:11434/api/chat -- Ollama n'est pas expose sur Internet (echec silencieux)

INC-7 : Redirect POST -> GET (Bug 3, non resolu cote client)

La lib Python requests suit les redirections 301/302 en transformant les POST en GET (RFC 7231). Quand NPM redirige http:// vers https://, tous les POST streaming (register, event, image, finalize) deviennent des GET et recevront un 405.

Aucune protection cote client. Le middleware serveur url_compat_rewrite ne resout que le probleme de path (pas le probleme de schema HTTP/HTTPS).


3. Recommandation architecturale

Principe : une seule variable, deux composants

RPA_SERVER_URL = URL complete incluant le prefixe API
                 Exemples :
                   http://localhost:5005/api/v1          (dev local)
                   http://192.168.1.40:5005/api/v1      (LAN)
                   https://lea.labs.laurinebazin.design/api/v1  (Internet)

Toutes les URLs de l'agent sont construites par concatenation de SERVER_URL + suffixe de route :

# config.py
SERVER_URL     = os.getenv("RPA_SERVER_URL", "http://localhost:5005/api/v1")
# Pour le health-check (route a la racine, pas sous /api/v1) :
SERVER_BASE    = SERVER_URL.rsplit("/api/v1", 1)[0]   # ex: "https://lea.labs.laurinebazin.design"

# streamer.py, executor.py, main.py : tous utilisent
STREAMING_ENDPOINT = f"{SERVER_URL}/traces/stream"    # inchange
HEALTH_ENDPOINT    = f"{SERVER_BASE}/health"           # NOUVEAU

# server_client.py : supprimer le systeme parallele
# _stream_base = SERVER_URL (plus de re-concatenation de "/api/v1" en dur)

Supprimer RPA_SERVER_HOST

Cette variable est source de confusion (INC-6). Elle ne doit pas exister. Le hostname est derive de RPA_SERVER_URL si besoin (pour l'affichage dans la chat window, etc.).

L'acces Ollama doit avoir sa propre variable RPA_OLLAMA_HOST (defaut : localhost), car Ollama n'est JAMAIS accessible via le reverse proxy Internet.

Protection POST -> GET

Ajouter dans streamer.py et executor.py :

# Dans chaque session requests :
session = requests.Session()
session.max_redirects = 0  # Refuser les redirections (echouer bruyamment)

Ou forcer https:// cote client si le host est un domaine public (pas localhost/IP privee).

Template config.txt

RPA_SERVER_URL=CONFIGURE_ME
RPA_API_TOKEN=CONFIGURE_ME
RPA_MACHINE_ID=CONFIGURE_ME

Pas de token reel, pas de valeur par defaut fonctionnelle. L'agent doit refuser de demarrer si RPA_SERVER_URL contient "CONFIGURE_ME".


4. Matrice scenarios x configuration

Scenario RPA_SERVER_URL RPA_API_TOKEN Notes
Dev local (meme machine) http://localhost:5005/api/v1 (vide ou token dev) RPA_AUTH_DISABLED=true cote serveur
LAN interne (Dom <-> VM) http://192.168.1.40:5005/api/v1 token prod HTTP OK en LAN ferme
Internet via NPM (TIM) https://lea.labs.laurinebazin.design/api/v1 token prod HTTPS obligatoire, pas de redirect
Futur DGX on-premise http://<ip_dgx>:5005/api/v1 ou https://... token prod Selon reseau client

5. Liste des fichiers a corriger

Priorite HAUTE (bloquant pour le deploiement TIM)

Fichier Ligne(s) Action
agent_v0/lea_ui/server_client.py 77-81, 161, 230, 287, 321, 349 Supprimer la double logique _stream_base. Utiliser SERVER_URL de config.py comme base, ne plus concatener /api/v1 en dur dans les appels. Pour /health, utiliser SERVER_BASE (sans /api/v1).
agent_v0/agent_v1/main.py 94-95 Supprimer l'utilisation de RPA_SERVER_HOST pour construire le LeaServerClient. Passer SERVER_URL directement.
agent_v0/agent_v1/main.py 370 Utiliser STREAMING_ENDPOINT au lieu de reconstruire le chemin manuellement.
agent_v0/agent_v1/network/streamer.py 34 Aucun changement (utilise deja STREAMING_ENDPOINT correctement).
deploy/lea_package/config.txt 14, 19, 20 Remplacer les valeurs par des placeholders CONFIGURE_ME. Supprimer le token reel.
deploy/installer/config_template.txt 26-27 Idem, remplacer le token reel par un placeholder.

Priorite MOYENNE (coherence du codebase)

Fichier Ligne(s) Action
agent_v0/agent_v1/config.py 43-45 Ajouter SERVER_BASE (URL sans /api/v1) pour le health-check.
agent_v0/agent_v1/core/executor.py 1144, 1280, 1595 Remplacer RPA_SERVER_HOST par une nouvelle var RPA_OLLAMA_HOST (defaut localhost).
web_dashboard/app.py 2055-2060 Renommer _RPA_PUBLIC_URL en _RPA_PUBLIC_SERVER_URL. S'assurer que le /api/v1 est toujours present dans le config.txt genere (deja fait, ligne 2097-2098).
web_dashboard/app.py 2119 Supprimer la ligne RPA_SERVER_HOST= du config.txt genere.

Priorite BASSE (nettoyage)

Fichier Action
agent_v0/config.py Supprimer ou marquer deprecated (ancien agent V0, port 8000).
agent_v0/deploy/windows_client/ Supprimer l'arborescence entiere (copie obsolete, remplacee par le ZIP Fleet).
agent_v0/deploy/windows_client/config.py Port 8000, endpoint /api/traces/upload -- completement mort.

Protection anti-redirect (Bug 3)

Fichier Action
agent_v0/agent_v1/network/streamer.py Utiliser requests.Session() avec max_redirects=0 ou forcer HTTPS si domaine public.
agent_v0/agent_v1/core/executor.py Idem pour les appels HTTP du replay (resolve_target, pre_analyze, replay/next, replay/result).

6. Diagramme de flux (etat cible)

  config.txt (genere par Fleet ou rempli a la main)
       |
       | RPA_SERVER_URL=https://lea.labs.laurinebazin.design/api/v1
       | RPA_API_TOKEN=<token>
       | RPA_MACHINE_ID=<id>
       | (plus de RPA_SERVER_HOST)
       |
       v
  Lea.bat -> set variables d'environnement
       |
       v
  agent_v1/config.py
       | SERVER_URL = "https://lea.labs.laurinebazin.design/api/v1"
       | SERVER_BASE = "https://lea.labs.laurinebazin.design"
       | STREAMING_ENDPOINT = "https://lea.labs.laurinebazin.design/api/v1/traces/stream"
       | HEALTH_ENDPOINT = "https://lea.labs.laurinebazin.design/health"
       |
       +-------> streamer.py    : STREAMING_ENDPOINT + "/register", "/event", "/image", "/finalize"
       +-------> main.py        : STREAMING_ENDPOINT + "/image" (heartbeat)
       |                          SERVER_URL (passe a executor.poll_and_execute)
       +-------> executor.py    : SERVER_URL + "/traces/stream/replay/next", etc.
       +-------> server_client.py : SERVER_URL + "/traces/stream/workflows", etc.
       |                            HEALTH_ENDPOINT (pour check_connection)
       |
       v (HTTPS, port 443)
  NPM Reverse Proxy
       |
       v (HTTP, port 5005)
  FastAPI api_stream.py
       | /health
       | /api/v1/traces/stream/register
       | /api/v1/traces/stream/event
       | /api/v1/traces/stream/image
       | /api/v1/traces/stream/replay/next
       | ...

7. Resume des bugs originaux et leur resolution

Bug Cause racine Correction
Bug 1 (URL obsolete dans config.txt) Template deploy/lea_package/config.txt jamais mis a jour DEJA CORRIGE (le fichier actuel contient /api/v1). Mais le token reel est toujours en clair.
Bug 2 (mismatch /api/v1) Deux modules (config.py vs server_client.py) avec des conventions incompatibles pour construire les URLs Unifier sur une seule convention : RPA_SERVER_URL inclut TOUJOURS /api/v1. server_client.py doit utiliser SERVER_URL de config.py au lieu de reimplementer sa propre logique.
Bug 3 (POST -> GET sur redirect) requests suit les 301 en changeant la methode HTTP Forcer HTTPS cote client quand le domaine est public, OU desactiver les redirections (max_redirects=0) pour echouer explicitement. Le middleware serveur url_compat_rewrite est un filet de securite pour le path, pas pour le schema.