# 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 x10–100, à 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 `. Le token contient en clair (avant hash) un préfixe identifiant : `lea___` pour faciliter le debug logs sans révéler le secret. Snippet de référence (Python serveur) : ```python 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**. ```python 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) : ```yaml 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: `. Promtail config : ```yaml 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///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 - [From PyInstaller to Nuitka: Convert Python to EXE Without False Positives (DEV.to)](https://dev.to/weisshufer/from-pyinstaller-to-nuitka-convert-python-to-exe-without-false-positives-19jf) - [How to Fix Antivirus False Positives with PyInstaller Executables (Python GUIs)](https://www.pythonguis.com/faq/problems-with-antivirus-software-and-pyinstaller/) - [Compilation vs Bundling: The Real Differences Between Nuitka and PyInstaller (KRRT7)](https://krrt7.dev/en/blog/nuitka-vs-pyinstaller) - [Best PyInstaller Alternatives 2026 (No AV Flags)](https://beatsyncpro.ai/alternative/pyinstaller.html) - [2026 Showdown: PyInstaller vs cx_Freeze vs Nuitka For Python EXE Builds](https://ahmedsyntax.com/2026-comparison-pyinstaller-vs-cx-freeze-vs-nui/) - [Nuitka Performance documentation](https://nuitka.net/user-documentation/performance.html) - [Nuitka Compilation: C-Level Python Performance Boost 2026](https://www.johal.in/nuitka-compilation-c-level-python-performance-boost-2026/) - [PyOxidizer has been abandoned (Anki Issue #3081)](https://github.com/ankitects/anki/issues/3081) - [Briefcase Windows MSI documentation](https://briefcase.beeware.org/en/stable/reference/platforms/windows/) - [GitHub - beeware/briefcase-windows-msi-template](https://github.com/beeware/briefcase-windows-msi-template) - [PyInstaller AV false positive Issue #6754](https://github.com/pyinstaller/pyinstaller/issues/6754) ### Code signing - [Azure Artifact Signing FAQ (Microsoft Learn)](https://learn.microsoft.com/en-us/azure/artifact-signing/faq) - [Azure Artifact Signing Pricing](https://azure.microsoft.com/en-us/pricing/details/artifact-signing/) - [Trusted Signing open for individual developers - Public Preview](https://techcommunity.microsoft.com/blog/microsoft-security-blog/trusted-signing-is-now-open-for-individual-developers-to-sign-up-in-public-previ/4273554) - [Trusted Signing 3-year requirement Q&A](https://learn.microsoft.com/en-us/answers/questions/2261318/is-there-any-exception-process-for-the-azure-trust) - [Fighting through Setting up Microsoft Trusted Signing (Rick Strahl)](https://weblog.west-wind.com/posts/2025/Jul/20/Fighting-through-Setting-up-Microsoft-Trusted-Signing) - [Code signing on Windows with Azure Trusted Signing (Melatonin)](https://melatonin.dev/blog/code-signing-on-windows-with-azure-trusted-signing/) - [Top 10 Best Code Signing Certificate Providers in 2026](https://sslinsights.com/best-code-signing-certificate-providers/) - [Sectigo EV Code Signing Certificate ($279.99/yr SignMyCode)](https://signmycode.com/sectigo-ev-code-signing) - [Sectigo EV Code Signing Certificates (TheSSLStore)](https://www.thesslstore.com/sectigo/sectigo-ev-code-signing-certificate.aspx) - [SignPath Pricing](https://about.signpath.io/product/pricing) - [SmartScreen reputation for Windows app developers (Microsoft Learn)](https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/smartscreen-reputation) - [Code signing options for Windows app developers (Microsoft Learn)](https://learn.microsoft.com/en-us/windows/apps/package-and-deploy/code-signing-options) - [Automate PyInstaller Builds and Code Signing (johanneskinzig)](https://johanneskinzig.com/automating-pyinstaller-builds-and-code-signing-with-powershell.html) ### Multi-tenant, auth, machine_id - [Multi-Tenant Architecture with FastAPI: Design Patterns and Pitfalls (Medium)](https://medium.com/@koushiksathish3/multi-tenant-architecture-with-fastapi-design-patterns-and-pitfalls-aa3f9e75bf8c) - [Multitenancy with FastAPI - A practical guide](https://app-generator.dev/docs/technologies/fastapi/multitenancy.html) - [Building Multi-Tenant APIs with FastAPI and Subdomain Routing](https://medium.com/@diwasb54/building-multi-tenant-apis-with-fastapi-and-subdomain-routing-a-complete-guide-cc076cb02513) - [FastAPI RBAC Permissions: Role-Based Access for ML Resources 2026](https://www.johal.in/fastapi-rbac-permissions-role-based-access-for-ml-resources-2026/) - [Get a unique computer ID in Python on Windows and Linux](https://www.iditect.com/faq/python/get-a-unique-computer-id-in-python-on-windows-and-linux.html) - [Get Windows Unique ID by Python](https://nashorn892087495.wordpress.com/2019/09/12/get-windows-unique-id-by-python/) - [JWT in FastAPI - Refresh Tokens Explained (Medium)](https://medium.com/@jagan_reddy/jwt-in-fastapi-the-secure-way-refresh-tokens-explained-f7d2d17b1d17) ### Silent install et auto-update - [Silent install cheatsheet (GitHub)](https://github.com/offlineinstallersetup/silent-install-cheatsheet) - [A Guide to Install MSI Silently (Server Scheduler)](https://serverscheduler.com/blog/install-msi-silently) - [Velopack - Cross-platform installer and auto-update framework](https://velopack.io/) - [Velopack documentation - Migrating from Squirrel](https://docs.velopack.io/migrating/squirrel) - [tufup - Automated updates for Python apps (GitHub)](https://github.com/dennisvang/tufup) - [PyUpdater archived](https://github.com/Digital-Sapphire/PyUpdater) ### Concurrents RPA - [UiPath Orchestrator About Licensing](https://docs.uipath.com/orchestrator/docs/about-licensing) - [UiPath Robot Licensing Standalone 2025.10](https://docs.uipath.com/robot/standalone/2025.10/admin-guide/licensing-troubleshooting) - [UiPath Automation Suite - Allocating Robot Licenses to Tenants](https://docs.uipath.com/automation-suite/docs/allocating-robot-and-service-licenses-to-tenants) - [Power Automate Desktop - Silent registration for machines](https://learn.microsoft.com/en-us/power-automate/desktop-flows/machines-silent-registration) - [Power Automate Desktop - Manage machine groups](https://learn.microsoft.com/en-us/power-automate/desktop-flows/manage-machine-groups) - [Power Automate Desktop - Hosted machines](https://learn.microsoft.com/en-us/power-automate/desktop-flows/hosted-machines) - [Automation Anywhere - RDP-based bot deployment](https://docs.automationanywhere.com/bundle/enterprise-v11.3/page/enterprise/topics/control-room/bots/my-bots/rdp-based-approach-to-bot-deployment.html) - [Skyvern Docker Setup](https://docs-new.skyvern.com/self-hosted/docker) - [Skyvern Issue #4392 - Multi-session VNC support](https://github.com/Skyvern-AI/skyvern/issues/4392) - [browser-use Issue #658 - Docker Image for Self Hosting](https://github.com/browser-use/browser-use/issues/658) ### Observabilité multi-tenant - [Grafana Loki - Manage tenant isolation](https://grafana.com/docs/loki/latest/operations/multi-tenancy/) - [Creating Multi-Tenant Observability Dashboards with Grafana & Loki (2025)](https://sollybombe.medium.com/creating-multi-tenant-observability-dashboards-with-grafana-loki-2025-edition-85a673eff596) - [Managing Grafana and Loki in a regulated multitenant environment (AWS)](https://aws.amazon.com/blogs/opensource/how-to-manage-grafana-and-loki-in-a-regulated-multitenant-environment/) ### HDS / RGPD / DSI hospitalière France - [Certification HDS en 2026 - ce que chaque hôpital doit vérifier (Galeon)](https://www.galeon.care/fr/blog/certification-hds-en-2026-ce-que-chaque-hopital-doit-verifier-avant-de-signer) - [HDS V2 certification framework (LSTI)](https://www.lsti-certification.fr/en/News/certification-HDS) - [HDS - Agence du Numérique en Santé](https://esante.gouv.fr/ens/offre/hds) - [Health Data Hosting (HDS) France - Microsoft Compliance](https://learn.microsoft.com/en-us/compliance/regulatory/offering-hds-france) - [Doctrine Numérique Santé 2025 - Règles de sécurité](https://esante.gouv.fr/doctrine/securite) --- *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.*