Files
rpa_vision_v3/docs/recherche/AXE_D4_MULTI_TENANT_DEPLOY.md

36 KiB
Raw Blame History

AXE D4 — Patterns de déploiement multi-tenant / multi-user d'un agent RPA Windows on-premise (2026)

Date : 2026-05-23 Auteur : Claude (agent de recherche, dispatché) Périmètre : packaging, code signing, multi-tenant, silent install, auto-update, observabilité, comparaison concurrents, plan en 3 paliers (POC 2 → 10 → 100+ postes). Statut : recherche en lecture seule, aucune modification de code. Sources cliquables en §10.


1. TL;DR — Recommandations immédiates

Packaging exe Windows : Nuitka commercial standalone pour Léa client (Agent V1). PyInstaller --onefile est à proscrire en milieu hospitalier : 100% des sources convergent sur le taux énorme de faux positifs antivirus, car le pattern self-extracting est indistinguable de malwares connus. Nuitka compile vers du C natif et obfusque le code Python — double bénéfice : moins de faux positifs ET protection IP modérée. Coût : temps de build x10100, à intégrer dans la CI.

Code signing : pour Phase 1 (POC AIVANOV + GHT démo) rester en non signé (statu quo). Pour Phase 2 (Anouste + 10 postes), Azure Trusted Signing = bloqué car réservé US/Canada + 3 ans d'ancienneté (pas applicable à un éditeur français). Acheter directement un certificat OV Sectigo chez un revendeur (CheapSSLSecurity ~120-200 €/an OV, ou 280-500 €/an EV via token HSM). L'EV n'apporte plus le bypass SmartScreen automatique depuis 2023 — l'OV suffit pour la confiance DSI, l'EV n'est utile que pour Windows Defender Application Control et certains pilotes signés. Pour la Phase 3 (>100 postes), envisager SignPath.io comme orchestrateur de signature (politiques + audit + intégration CI).

Multi-tenant 3 paliers :

  1. POC 2 postes (actuel) : lea / Medecin2026! HTTP Basic statique, machine_id = UUID WMI wmic csproduct get uuid. Ne pas durcir.
  2. 10 postes Anouste : table users + client_machines + tokens API par machine (Fernet, rotation manuelle), JWT pour le dashboard, RBAC 3 rôles (admin / superviseur / viewer). Logs par tenant via préfixe fichier.
  3. 100+ postes GHT/ARS : Postgres + Row-Level Security ou schéma par tenant, refresh tokens avec rotation auto, observabilité Loki/Grafana avec header X-Scope-OrgID, SCIM pour AD/Entra, audit trail signé.

Dépendances explicites :

  • AXE_B1 (transport HTTP → SSE/WebSocket) : tout watchdog et révocation de token dépendent de la couche transport — un client en long-poll qui rate l'ACK ne peut pas être proprement révoqué.
  • AXE_B5_D1 (capture distante NoMachine/AnyDesk) : la confusion "agent Léa" vs "outil de prise en main" doit rester explicite dans le packaging (nom binaire lea-agent.exe, jamais rpa-controller.exe ou autre nom qui prête à confusion DSI).

2. Packaging Python → exe Windows (2026)

2.1. Table comparative

Outil Maturité 2026 Taille .exe Léa (estim.) Démarrage à froid Faux positifs AV Build time Recommandation
PyInstaller --onefile très mature 80-150 Mo (PyQt5+mss+pynput) 3-8 s (extraction temp) élevé (heuristique self-extract) rapide (< 1 min) À éviter
PyInstaller --onedir très mature 200 Mo dossier < 1 s moyen rapide Acceptable si MSI
Nuitka standalone mature 2026 100-180 Mo < 1 s (binaire natif) faible (binaire C) lent (10-30 min) Recommandé
Nuitka onefile mature 2026 100-150 Mo 2-4 s faible lent OK pour distrib
Briefcase BeeWare mature (WiX 5.0.2) 80-120 Mo MSI natif < 1 s moyen moyen Très bon pour MSI/AD
cx_Freeze maintenu, marginal 100 Mo < 1 s moyen moyen Pas de raison de le choisir
PyOxidizer abandonné (dernier commit jan 2023) Ne pas adopter

2.2. Recommandation détaillée pour Léa

Léa Agent V1 = client léger Python (capture mss, pynput, requests/websocket, PyQt5 system tray). Pas de Transformers ni torch côté client (resté sur le serveur GPU).

Choix recommandé : Nuitka commercial standalone, packagé dans un MSI WiX.

Pourquoi cette combinaison :

  • Nuitka élimine le pattern self-extract qui fait que PyInstaller --onefile est régulièrement signalé par Windows Defender / Kaspersky / Trend Micro en milieu hospitalier. C'est confirmé par plusieurs retours 2025-2026 (cf. sources). Compilation en C natif → l'exécutable ressemble à un binaire C/Rust normal.
  • MSI WiX via Briefcase OU MSI WiX maison autour du build Nuitka : MSI est le format attendu par les DSI hospitalières pour déploiement GPO/SCCM/Intune. Évite la friction "exécutable inconnu" et permet l'installation silencieuse msiexec /i lea-agent.msi /qn ALLUSERS=1.

Variante de transition : si l'effort Nuitka est trop lourd à mettre en CI dans le mois qui vient pour Anouste, accepter PyInstaller --onedir (dossier, pas onefile) packagé dans un MSI WiX. Le --onedir a des taux de faux positifs nettement inférieurs au --onefile car il n'y a pas de bootloader d'extraction.

Liste de paquets exclus du bundle Léa (à expliciter dans le .spec Nuitka/PyInstaller pour réduire la taille) :

  • torch, transformers, triton, nvidia-* (côté serveur uniquement)
  • faiss, sentence-transformers (côté serveur)
  • docTR, easyocr (côté serveur — sauf si Léa fait OCR local prévu en P3)
  • modules tests, pytest, notebook, jupyter*

2.3. Tests à mener avant de figer le choix

  • Build Léa minimal (capture + envoi HTTP) en PyInstaller --onedir, PyInstaller --onefile, Nuitka standalone, Nuitka onefile.
  • Soumission à VirusTotal de chaque artefact (les 4 binaires). Cible : 0/72 sur Nuitka, <5/72 sur PyInstaller onedir, échec confirmé sur PyInstaller onefile.
  • Mesure démarrage à froid sur un poste Windows 10 (Pauline) et Windows 11 (TIM Anoust).
  • Mesure RAM résidente.

3. Code signing 2026

3.1. Table comparative des CA et services

Solution Type Prix annuel (HT) Bypass SmartScreen KYC France Verdict pour Léa
Pas de signature 0 € Non, "Plus d'infos → Exécuter quand même" OK Phase 1
Azure Trusted Signing (ex Azure Artifact Signing) OV-like (non EV) ~120 € (10 €/mois Basic) Réputation progressive, pas immédiat Bloqué : US/Canada uniquement, +3 ans d'ancienneté requis NON applicable
Sectigo OV (via revendeur SignMyCode/CheapSSL) OV 120-200 € Réputation progressive OK (KGI standard) Bon choix Phase 2
Sectigo EV (token HSM ou eToken) EV 280-500 € Plus de bypass auto depuis 2023, mais meilleure réputation initiale OK + KYC renforcé (Dun & Bradstreet, registre commerce) Si DSI exige EV
DigiCert EV EV 500-700 € Idem Sectigo EV OK + KYC renforcé Plus cher, image premium
SignPath.io Orchestrateur (utilise CA tierce) dès ~50 €/mois (Foundation gratuit pour open-source) dépend du certificat sous-jacent Indirect via CA Pertinent Phase 3 (audit + CI/CD + politiques signature)
Whitelist SHA256 DSI hors CA 0 € DSI pousse le hash via GPO Defender ASR Discussion contractuelle Plan A documenté (Anoust)

3.2. Changements 2026 à intégrer

  • CA/B Forum 15 février 2026 : les certificats de signature de code à 2 et 3 ans sont désormais réservés à l'option "Install on Existing HSM" (le client a déjà son HSM/yubikey). L'option Token + Shipping est limitée à 1 an. Pour Léa, prévoir un renouvellement annuel ou un investissement HSM (YubiKey 5C FIPS ~95 €).
  • EV ne bypasse plus SmartScreen automatiquement depuis 2023. Le différentiel EV vs OV ne se justifie que pour : pilotes signés, Windows Defender Application Control en mode allow-listing certaines DSI, ou imagerie pro.
  • Azure Trusted Signing : a beaucoup bougé 2025-2026 mais reste fermé aux entreprises françaises pendant toute la durée du POC. À surveiller mais pas planifier dessus.

3.3. Plan d'acquisition recommandé pour un éditeur santé français

Phase 1 (maintenant, GHT démo + AIVANOV) : aucune signature. Documenter dans deploy/installer/README.md la procédure "Plus d'infos → Exécuter quand même" + capture d'écran.

Phase 2 (Anouste, 10 postes) :

  1. Acheter Sectigo OV Code Signing 1 an via CheapSSLSecurity ou SignMyCode (~130-180 € HT). Validation : 3-7 jours ouvrés avec extrait Kbis + facture pro + appel téléphonique de vérification.
  2. En parallèle, ouvrir la négociation avec Fabrice DUPOUY (DSI Anoust) sur la whitelist SHA256 dans Defender + leur antivirus principal. Combiner : DSI whitelist + signature OV = sécurité maximale + 0 friction utilisateur final.
  3. Documenter le hash SHA256 du MSI dans la docstring du release tag git.

Phase 3 (GHT/ARS, 100+ postes) :

  1. Passer à Sectigo EV Code Signing avec HSM (YubiKey FIPS) ou DigiCert EV si DSI hospitalier exige une CA "tier 1 reconnue".
  2. Intégrer SignPath.io dans la CI GitHub Actions / Gitea Actions : politique de signature (qui peut signer, depuis quelle branche, quel commit signataire), logs d'audit consultables, intégration avec le HSM cloud.

3.4. Recommandation transverse

Ne pas attendre Phase 3 pour signer. Une signature OV obtenue dès Phase 2 commence à accumuler de la réputation SmartScreen / Defender (le binaire devient connu). Plus tôt on signe, plus tôt la friction disparaît.


4. Multi-tenant — modèle de données, tokens, machine_id, auth

4.1. Modèle de données recommandé (Phase 2-3)

tenants (id PK, name, contact_email, status, hds_certified BOOL, created_at)
users (id PK, tenant_id FK, username, password_hash BCRYPT, role ENUM admin|superviseur|viewer, totp_secret NULL, last_login, created_at)
client_machines (id PK, tenant_id FK, machine_id UNIQUE, hostname, os_version, user_id FK NULLABLE, status ENUM active|revoked|paused, api_token_hash, token_expires_at, last_seen)
api_tokens_audit (id PK, machine_id FK, action ENUM created|rotated|revoked, actor_user_id FK, timestamp, reason TEXT)
workflows_per_tenant (id PK, tenant_id FK, workflow_name, version)
audit_log (id PK, tenant_id FK, machine_id FK NULLABLE, user_id FK NULLABLE, action, payload_json, timestamp)

Trois patterns d'isolation possibles, par ordre croissant d'isolation et de complexité :

  • Shared DB + tenant_id column : 1 base, 1 schéma, colonne tenant_id partout + RLS Postgres. Recommandé Phase 2-3.
  • Shared DB + schema per tenant : 1 base, N schémas. Plus d'isolation mais migrations N fois.
  • DB per tenant : isolation maximale, coût opérationnel élevé. À garder pour clients exigeants (ARS, ministériel).

Phase 2 (SQLite actuel) : viable si moins de 50 postes total, mais migration Postgres dès qu'on dépasse 5 tenants.

4.2. Génération de tokens API par machine

Pattern recommandé : token aléatoire 32 bytes URL-safe, stocké hashé (Argon2id ou SHA-256+sel) en base, présenté à chaque requête en header Authorization: Bearer <token>. Le token contient en clair (avant hash) un préfixe identifiant : lea_<tenant_id>_<machine_id_short>_<random_32> pour faciliter le debug logs sans révéler le secret.

Snippet de référence (Python serveur) :

import secrets
import hashlib
from datetime import datetime, timedelta

def generate_machine_token(tenant_id: str, machine_id: str) -> tuple[str, str]:
    """Retourne (token_clair, token_hash). Stocker uniquement le hash."""
    random_part = secrets.token_urlsafe(32)
    token = f"lea_{tenant_id}_{machine_id[:8]}_{random_part}"
    token_hash = hashlib.sha256(token.encode()).hexdigest()
    return token, token_hash

def verify_token(token_clair: str, stored_hash: str) -> bool:
    return hashlib.sha256(token_clair.encode()).hexdigest() == stored_hash

Rotation : tokens long-lived (90 jours) côté machine, refresh manuel depuis dashboard admin. Pour Phase 3 : pattern OAuth2 client credentials + refresh tokens avec rotation à chaque usage (TOTP-like).

Révocation : flag status='revoked' en base + cache court (60s) côté serveur. Avec le transport HTTP pull/long-poll actuel, la révocation prend effet au prochain poll (5-30s). Avec SSE/WebSocket (AXE_B1), le serveur peut fermer la connexion immédiatement à la révocation.

4.3. machine_id unique Windows

Une seule source ne suffit pas (UUID virtualisé, MAC qui change, hostname dupliqué chez les vraies clinique). Recommandation : identifiant composite stable.

import uuid
import subprocess
import socket
import hashlib

def get_machine_id_composite() -> str:
    """ID composite stable sur Windows, fallback Linux/macOS."""
    components = []

    # 1. WMI UUID (Win32_ComputerSystemProduct) — stable même après reset partiel
    try:
        out = subprocess.check_output(
            ["wmic", "csproduct", "get", "uuid"],
            timeout=5, stderr=subprocess.DEVNULL
        ).decode().strip().split("\n")
        if len(out) > 1:
            wmi_uuid = out[1].strip()
            if wmi_uuid and "FFFFFFFF" not in wmi_uuid:
                components.append(wmi_uuid)
    except Exception:
        pass

    # 2. MAC address de la première interface active (uuid.getnode)
    mac = uuid.getnode()
    if (mac >> 40) % 2 == 0:  # éviter les MAC aléatoires (bit local)
        components.append(str(mac))

    # 3. Hostname (NetBIOS / DNS)
    components.append(socket.gethostname())

    if not components:
        # Fallback : MachineGuid registre Windows
        try:
            out = subprocess.check_output(
                ["reg", "query", r"HKLM\SOFTWARE\Microsoft\Cryptography", "/v", "MachineGuid"],
                timeout=5
            ).decode()
            for line in out.split("\n"):
                if "MachineGuid" in line:
                    components.append(line.split()[-1])
        except Exception:
            components.append(str(uuid.uuid1()))  # ultime fallback

    raw = "|".join(components)
    return hashlib.sha256(raw.encode()).hexdigest()[:32]

Important : ne JAMAIS cloner une VM avec Léa installée sans regenerer le machine_id (cf. doc Power Automate Desktop : "if you reset your PC, your machine registration will be lost"). Prévoir un script python -m lea_agent.reset_machine_id documenté.

4.4. Pattern auth bouchon → cible

État actuel (bouchon, Phase 1) : DASHBOARD_USER=lea, DASHBOARD_PASSWORD=Medecin2026! en HTTP Basic. Un token global Bearer dans .env.local côté NPM reverse proxy pour lea.labs.laurinebazin.design.

Cible Phase 2 (3 jours d'effort) :

  1. Migration SQLite → table users + client_machines.
  2. flask-login ou fastapi-users pour sessions dashboard (cookies signés).
  3. Endpoint admin POST /api/v1/admin/machines qui génère un token, retourne le clair UNE seule fois, stocke le hash.
  4. Endpoint client GET /api/v1/replay/next qui vérifie Authorization: Bearer ... et update last_seen.
  5. Page dashboard /admin/machines : liste, révoquer, rotation, télécharger ZIP installer pré-configuré avec le token.

Cible Phase 3 (1 semaine d'effort) :

  1. Postgres + RLS, JWT avec refresh tokens (durée courte access 15 min, refresh 7 jours).
  2. SCIM pour synchroniser AD/Entra (utilisateurs hospitaliers).
  3. TOTP RFC 6238 pour les admins (déjà câblé dans core/auth/, à exposer).
  4. Audit trail signé (chaîne de hash) pour conformité HDS V2.

5. Déploiement silent install + auto-update

5.1. Silent install Windows entreprise

MSI > exe self-extracting en hôpital. Les DSI hospitalières utilisent Intune, SCCM ou GPO, qui sont câblées sur MSI.

Commande de référence (à documenter dans le dossier DSI Anoust) :

msiexec /i Lea-Agent-v1.0.0.msi /qn /norestart ALLUSERS=1 \
    LEA_SERVER_URL=https://lea.anoust.fr \
    LEA_MACHINE_TOKEN=lea_anoust_a3b9c2_xxxxxxxxx \
    INSTALLDIR="C:\Program Files\Lea Agent" \
    /l*v "C:\Windows\Temp\lea-install.log"

Patterns DSI hospitalier :

  • Déploiement par OU (Organisational Unit) AD pour cibler "Postes TIM" ou "Postes Médecins urgences".
  • Per-machine install (ALLUSERS=1) car les RPA tournent sous SYSTEM ou compte de service.
  • Pré-provisionning du token à l'installation : éviter le pattern "lance Léa, copie-colle un token". À la place, le ZIP téléchargé depuis le dashboard contient un MSI avec le token déjà intégré (custom MSI per machine).

Si MSI trop complexe en Phase 2 : Inno Setup (gratuit, plus simple que WiX) avec /VERYSILENT /SUPPRESSMSGBOXES accepte les déploiements GPO/Intune en mode "Win32 app" Intune. Briefcase BeeWare expose WiX 5.0.2 sans complexité, c'est probablement la voie la plus simple si on reste Python-first.

5.2. Auto-update — recommandation

Pour Phase 2 (10 postes Anouste) : pas d'auto-update automatique. Procédure manuelle : pousser un nouveau MSI via SCCM/Intune ou via un script PowerShell que la DSI exécute. Notification dans le tray Léa "Une nouvelle version est disponible, contactez votre DSI".

Pour Phase 3 (100+ postes) : framework d'auto-update.

Framework Pertinence Léa Verdict
Velopack Réécriture moderne de Squirrel.Windows en Rust, delta updates, multi-langage, multi-plateforme. Actif 2025-2026. Recommandé
Squirrel.Windows Historique .NET, maintenu mais Velopack est le successeur. À éviter (legacy)
tufup Successeur Python de PyUpdater, basé sur python-tuf (sécurisé par design), indépendant du packaging. Très pertinent si on reste Python natif et qu'on ne passe pas par Velopack.
PyUpdater Archivé, ne pas utiliser. À proscrire

Recommandation finale : tufup. Avantages pour Léa :

  • Indépendant du packaging (compatible Nuitka et PyInstaller),
  • Sécurité basée sur TUF (The Update Framework) — signature des manifests, protection contre rollback attacks,
  • Maintenance active 2025-2026,
  • Manifest hosting peut être le serveur Linux on-premise existant.

Pattern d'update : side-by-side (installer dans C:\Program Files\Lea Agent\v1.0.1\ à côté de v1.0.0\), bascule du raccourci système au prochain redémarrage. Rollback = pointer le raccourci vers la version précédente, qui reste sur disque pendant N versions (paramétrable). Avantage : zéro corruption sur update interrompue, rollback en < 5 minutes.

Audit trail update : chaque agent log dans audit_log côté serveur le succès/échec de l'update, version source, version cible, durée. Permet d'alerter si > 5% de la flotte est sur une ancienne version.


6. Stack serveur multi-tenant on-premise

6.1. docker-compose multi-tenant (squelette de référence)

Pour Phase 3 (le stack actuel svc.sh reste valable Phase 1-2) :

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: lea_main
      POSTGRES_USER: lea_admin
      POSTGRES_PASSWORD_FILE: /run/secrets/pg_password
    volumes:
      - pg_data:/var/lib/postgresql/data
    networks: [lea_internal]

  api_stream:
    image: rpa-vision/api-stream:1.0.0
    environment:
      DATABASE_URL: postgresql://lea_admin@postgres/lea_main
      TENANT_RESOLUTION_HEADER: X-Lea-Tenant-Id
    networks: [lea_internal, lea_public]
    depends_on: [postgres]

  ollama:
    image: ollama/ollama:latest
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
    volumes:
      - ollama_models:/root/.ollama
    networks: [lea_internal]

  loki:
    image: grafana/loki:3.0.0
    command: -config.file=/etc/loki/loki-config.yaml
    volumes:
      - ./loki-config.yaml:/etc/loki/loki-config.yaml
      - loki_data:/loki
    networks: [lea_internal]
    # auth_enabled: true dans loki-config.yaml

  promtail:
    image: grafana/promtail:3.0.0
    volumes:
      - /var/log:/var/log
      - ./promtail.yaml:/etc/promtail/config.yaml
    networks: [lea_internal]

  grafana:
    image: grafana/grafana:11.0.0
    networks: [lea_internal, lea_public]
    environment:
      GF_AUTH_GENERIC_OAUTH_ENABLED: "true"
      # SSO via Keycloak ou Entra

6.2. Observabilité par tenant

Logs (Loki) : auth multi-tenant activé (auth_enabled: true), chaque pod/agent envoie X-Scope-OrgID: <tenant_id>. Promtail config :

clients:
  - url: http://loki:3100/loki/api/v1/push
    tenant_id: ${LEA_TENANT_ID}

Côté Grafana : un datasource Loki par tenant, ou un seul datasource avec le tenant_id injecté par variable dashboard.

Métriques (Prometheus) : labels tenant_id="anoust", machine_id="abc123...". Quotas par tenant via Cortex/Mimir si on monte en charge.

Audit trail RGPD/HDS : tableau audit_log partitionné par tenant + signature SHA-256 chaînée (chaque ligne contient le hash de la précédente). Conservation : 3 ans en hot, 7 ans en cold (S3 ou disque externe), conforme HDS V2.

6.3. Secrets management

Phase 1-2 : .env.local + chmod 600. C'est OK tant qu'on est < 5 tenants.

Phase 3 : HashiCorp Vault self-hosted (image officielle dispo) ou age + secrets chiffrés en git. Pour les tokens API machines, déjà couvert par le hash en base. Pour les mots de passe Citrix/Easily Assure, utiliser core/auth/credential_vault.py déjà câblé (Fernet+PBKDF2).

6.4. HDS V2 — implications concrètes

Si Léa stocke des screenshots patients côté serveur ET que ce serveur est on-premise hôpital, l'hôpital est l'hébergeur (HDS interne) et n'a pas besoin de certifier l'éditeur. Si le serveur est chez Dom / chez le client / dans un cloud, certification HDS V2 obligatoire (article L1111-8 CSP). Implication produit : prioriser le déploiement on-premise hôpital pour Phase 2-3, le SaaS multi-tenant centralisé est un sujet HDS-lourd (~30-60 k€ d'audit + 6 mois) à n'envisager qu'après stabilisation produit.


7. Comparaison concurrents — multi-tenant et déploiement

7.1. UiPath Robot

Modèle : licence par runtime (Production Unattended), allocation par tenant (Orchestrator → Admin → Tenants → Edit license allocation). Le robot consomme des runtimes du pool tenant à la connexion, les libère à la déconnexion. Limite : robot licences cantonnées à UN tenant — pas de "robot multi-tenant". Pour un hôpital multi-établissements GHT, on créerait 1 tenant par établissement.

Pattern à reprendre : pool de runtimes au niveau tenant, allocation dynamique. Pertinent pour Phase 3 GHT.

7.2. Power Automate Desktop (Microsoft)

Modèle : silent registration des machines via Power-Automate-machine-runtime.exe /SILENT REGISTRATIONKEY=xxxx. Service principal pour l'enrôlement bulk. Multi-session sur Windows Server (RDS) pour scaler à 10-20 bots par machine physique. Limite critique : si on clone une VM avec PAD installé, casse l'enrôlement.

Pattern à reprendre : enrôlement silencieux avec clé pré-générée, interdiction de cloner les VM documentée.

7.3. Automation Anywhere Bot Runner

Modèle : Bot Runners = comptes utilisateurs taggés "runner license" dans Control Room. Déploiement via RDP-based : le Control Room ouvre une session RDP sur la machine cible et lance le bot. Device Pools = groupes logiques pour distribuer la charge.

Pattern à reprendre : Device Pools pour parallélisation (intéressant pour un déploiement multi-postes TIM dans le même service).

7.4. Skyvern (open source, le plus proche de nous)

Modèle : docker-compose postgres + skyvern (API+browser) + skyvern-ui. Self-hosting complet possible. Limite documentée : pas de VNC multi-session en self-hosted, problème d'observabilité multi-bot. Pas de tenancy native dans la version open-source (à coder soi-même).

Pattern à reprendre : isolation par container Docker pour chaque session active, pratique pour scaler horizontalement. Différentiel pour nous : Léa tourne sur Windows utilisateur, pas dans un container — donc le pattern Skyvern ne s'applique qu'au serveur.

7.5. browser-use

Modèle : Cloud Skyvern-like, self-hosting Docker en cours de maturation (issue #658 GitHub février 2025). Multi-tenant : non documenté officiellement, communauté demande.

À surveiller mais pas d'inspiration directe en 2026.

7.6. Stack hospitalière française (Dedalus, Maincare, Easily Assure)

Dedalus et Maincare déploient leurs DPI via MSI signés + GPO. Auth utilisateur via SSO LDAP/Active Directory (Kerberos). Pas de modèle "agent par poste" car le DPI est web ou client-serveur — pas applicable directement.

Easily Assure : client lourd Windows (.NET) + serveur central. Authentification par compte utilisateur (couplé AD).

Implication pour Léa : nos clients DSI savent gérer GPO + MSI signé + AD SSO. C'est le standard attendu. Notre dispositif actuel (token Bearer envoyé manuellement) est en dessous des standards de la branche.


8. Plan en 3 paliers

Palier 1 — POC 2 postes (actuel, mai 2026)

État : 1 démo + 1 dev. lea / Medecin2026! HTTP Basic, MSI absent, .exe PyInstaller --onefile non signé, install manuel via AnyDesk.

Action : ne rien casser. Documenter clairement le delta produit/cible.

Effort : 0.

Palier 2 — 10 postes Anouste + 5 postes GHT pilote (T3 2026)

À livrer :

  1. Packaging : migration Nuitka standalone (commercial, ~250 €/an) ou PyInstaller --onedir packagé MSI Briefcase. Cible : binaire signé OV, démarrage < 2s, taille < 200 Mo.
  2. Code signing : achat Sectigo OV 1 an (~150 €) + négociation whitelist SHA256 avec DSI Anoust en parallèle (filet de sécurité 0 €).
  3. Multi-tenant : migration SQLite → tables tenants, users, client_machines, api_tokens_audit. flask-login pour dashboard. 3 rôles (admin / superviseur / viewer).
  4. machine_id composite (snippet §4.3) intégré au démarrage agent.
  5. Token API par machine généré depuis dashboard admin, MSI custom par machine.
  6. Logs par tenant : préfixe fichier logs/<tenant_id>/<machine_id>/agent.log. Rotation logrotate.
  7. Silent install MSI documenté pour DSI Anoust.
  8. Audit trail basique : table audit_log (qui a lancé quel replay sur quelle machine).
  9. Procédure update manuelle documentée (DSI pousse le MSI).

Effort estimé : 10-15 jours dev, 3-5 jours validation site Anoust.

Dépendance critique : AXE_B1 transport. Tant qu'on est en HTTP long-poll, la révocation de token a une latence 5-30s. Acceptable Phase 2 mais à corriger Phase 3.

Palier 3 — 100+ postes GHT/ARS (T4 2026 - T2 2027)

À livrer :

  1. Postgres + RLS ou schéma par tenant (selon profil clients).
  2. JWT access + refresh tokens avec rotation (15 min / 7 jours).
  3. SCIM pour synchroniser AD/Entra hospitalier.
  4. TOTP pour admins (déjà câblé dans core/auth/, à exposer).
  5. SignPath.io pour orchestrer signature CI/CD + audit (~600 €/an).
  6. Sectigo EV ou DigiCert EV avec HSM YubiKey (~400 €/an).
  7. tufup pour auto-update side-by-side + rollback.
  8. Loki + Grafana observabilité avec X-Scope-OrgID.
  9. Audit trail signé chaîné SHA-256 pour HDS V2.
  10. Quotas par tenant (nombre de replays/h, taille captures, etc.).
  11. Onboarding self-service tenant via dashboard admin global (création tenant, premier admin, premier token).

Effort estimé : 30-60 jours dev, 10-15 jours sécurité/audit.

Décision HDS à trancher : on-premise hôpital systématique (pas de certification HDS éditeur requise) vs SaaS centralisé (HDS V2 obligatoire, 30-60 k€).


9. Restitution finale (< 250 mots)

Packaging : passer Léa de PyInstaller --onefile (faux positifs AV massifs en milieu hospitalier) à Nuitka standalone (compilation C native, faux positifs faibles) packagé dans un MSI WiX via Briefcase pour déploiement GPO/Intune/SCCM. Étape transitoire acceptable : PyInstaller --onedir dans MSI WiX.

Code signing — Phase 1 : rester non signé (statu quo). Phase 2 Anouste : achat Sectigo OV 1 an ~150 € HT + négociation whitelist SHA256 DSI en parallèle. Phase 3 : Sectigo EV avec HSM YubiKey + orchestration SignPath.io (~600 €/an). Azure Trusted Signing bloqué car réservé US/Canada + 3 ans d'ancienneté — ne pas planifier dessus, contrairement à ce qui était espéré dans project_code_signing.md.

Multi-tenant 3 paliers :

  1. POC 2 postes : statu quo, machine_id = UUID WMI.
  2. 10 postes : tables users/client_machines, tokens API par machine, 3 rôles RBAC, flask-login, silent MSI, logs par tenant.
  3. 100+ postes : Postgres + RLS, JWT refresh, SCIM AD, TOTP admins, tufup auto-update, Loki/Grafana multi-tenant, audit trail signé HDS V2.

Dépendances :

  • AXE_B1 (transport HTTP → SSE/WebSocket) : prérequis pour révocation de token immédiate et fermeture de session propre. Avec HTTP long-poll, latence 5-30s sur révocation.
  • AXE_B5_D1 (capture distante) : ne pas brouiller le nom du binaire (Léa ≠ outil de prise en main DSI).

HDS V2 : on-premise hôpital systématique en Phase 2-3 pour éviter le coût de certification HDS éditeur (30-60 k€, 6 mois). SaaS centralisé hors périmètre tant que produit non stabilisé.


10. Sources

Packaging Python → exe

Code signing

Multi-tenant, auth, machine_id

Silent install et auto-update

Concurrents RPA

Observabilité multi-tenant

HDS / RGPD / DSI hospitalière France


Document destiné à être consommé en lecture seule par Dom comme support de décision sur les axes packaging, code signing et architecture multi-tenant. Pas d'action engagée. Validation explicite requise avant chaque étape de Palier 2 ou 3.