36 KiB
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 :
- POC 2 postes (actuel) :
lea / Medecin2026!HTTP Basic statique, machine_id = UUID WMIwmic csproduct get uuid. Ne pas durcir. - 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. - 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, jamaisrpa-controller.exeou 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 --onefileest 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) :
- 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.
- 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.
- Documenter le hash SHA256 du MSI dans la docstring du release tag git.
Phase 3 (GHT/ARS, 100+ postes) :
- Passer à Sectigo EV Code Signing avec HSM (YubiKey FIPS) ou DigiCert EV si DSI hospitalier exige une CA "tier 1 reconnue".
- 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_idpartout + 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) :
- Migration SQLite → table
users+client_machines. flask-loginoufastapi-userspour sessions dashboard (cookies signés).- Endpoint admin
POST /api/v1/admin/machinesqui génère un token, retourne le clair UNE seule fois, stocke le hash. - Endpoint client
GET /api/v1/replay/nextqui vérifieAuthorization: Bearer ...et updatelast_seen. - 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) :
- Postgres + RLS, JWT avec refresh tokens (durée courte access 15 min, refresh 7 jours).
- SCIM pour synchroniser AD/Entra (utilisateurs hospitaliers).
- TOTP RFC 6238 pour les admins (déjà câblé dans
core/auth/, à exposer). - 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 :
- Packaging : migration Nuitka standalone (commercial, ~250 €/an) ou PyInstaller
--onedirpackagé MSI Briefcase. Cible : binaire signé OV, démarrage < 2s, taille < 200 Mo. - Code signing : achat Sectigo OV 1 an (~150 €) + négociation whitelist SHA256 avec DSI Anoust en parallèle (filet de sécurité 0 €).
- Multi-tenant : migration SQLite → tables
tenants,users,client_machines,api_tokens_audit.flask-loginpour dashboard. 3 rôles (admin / superviseur / viewer). - machine_id composite (snippet §4.3) intégré au démarrage agent.
- Token API par machine généré depuis dashboard admin, MSI custom par machine.
- Logs par tenant : préfixe fichier
logs/<tenant_id>/<machine_id>/agent.log. Rotation logrotate. - Silent install MSI documenté pour DSI Anoust.
- Audit trail basique : table
audit_log(qui a lancé quel replay sur quelle machine). - 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 :
- Postgres + RLS ou schéma par tenant (selon profil clients).
- JWT access + refresh tokens avec rotation (15 min / 7 jours).
- SCIM pour synchroniser AD/Entra hospitalier.
- TOTP pour admins (déjà câblé dans
core/auth/, à exposer). - SignPath.io pour orchestrer signature CI/CD + audit (~600 €/an).
- Sectigo EV ou DigiCert EV avec HSM YubiKey (~400 €/an).
- tufup pour auto-update side-by-side + rollback.
- Loki + Grafana observabilité avec
X-Scope-OrgID. - Audit trail signé chaîné SHA-256 pour HDS V2.
- Quotas par tenant (nombre de replays/h, taille captures, etc.).
- 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 :
- POC 2 postes : statu quo, machine_id = UUID WMI.
- 10 postes : tables
users/client_machines, tokens API par machine, 3 rôles RBAC,flask-login, silent MSI, logs par tenant. - 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)
- How to Fix Antivirus False Positives with PyInstaller Executables (Python GUIs)
- Compilation vs Bundling: The Real Differences Between Nuitka and PyInstaller (KRRT7)
- Best PyInstaller Alternatives 2026 (No AV Flags)
- 2026 Showdown: PyInstaller vs cx_Freeze vs Nuitka For Python EXE Builds
- Nuitka Performance documentation
- Nuitka Compilation: C-Level Python Performance Boost 2026
- PyOxidizer has been abandoned (Anki Issue #3081)
- Briefcase Windows MSI documentation
- GitHub - beeware/briefcase-windows-msi-template
- PyInstaller AV false positive Issue #6754
Code signing
- Azure Artifact Signing FAQ (Microsoft Learn)
- Azure Artifact Signing Pricing
- Trusted Signing open for individual developers - Public Preview
- Trusted Signing 3-year requirement Q&A
- Fighting through Setting up Microsoft Trusted Signing (Rick Strahl)
- Code signing on Windows with Azure Trusted Signing (Melatonin)
- Top 10 Best Code Signing Certificate Providers in 2026
- Sectigo EV Code Signing Certificate ($279.99/yr SignMyCode)
- Sectigo EV Code Signing Certificates (TheSSLStore)
- SignPath Pricing
- SmartScreen reputation for Windows app developers (Microsoft Learn)
- Code signing options for Windows app developers (Microsoft Learn)
- Automate PyInstaller Builds and Code Signing (johanneskinzig)
Multi-tenant, auth, machine_id
- Multi-Tenant Architecture with FastAPI: Design Patterns and Pitfalls (Medium)
- Multitenancy with FastAPI - A practical guide
- Building Multi-Tenant APIs with FastAPI and Subdomain Routing
- FastAPI RBAC Permissions: Role-Based Access for ML Resources 2026
- Get a unique computer ID in Python on Windows and Linux
- Get Windows Unique ID by Python
- JWT in FastAPI - Refresh Tokens Explained (Medium)
Silent install et auto-update
- Silent install cheatsheet (GitHub)
- A Guide to Install MSI Silently (Server Scheduler)
- Velopack - Cross-platform installer and auto-update framework
- Velopack documentation - Migrating from Squirrel
- tufup - Automated updates for Python apps (GitHub)
- PyUpdater archived
Concurrents RPA
- UiPath Orchestrator About Licensing
- UiPath Robot Licensing Standalone 2025.10
- UiPath Automation Suite - Allocating Robot Licenses to Tenants
- Power Automate Desktop - Silent registration for machines
- Power Automate Desktop - Manage machine groups
- Power Automate Desktop - Hosted machines
- Automation Anywhere - RDP-based bot deployment
- Skyvern Docker Setup
- Skyvern Issue #4392 - Multi-session VNC support
- browser-use Issue #658 - Docker Image for Self Hosting
Observabilité multi-tenant
- Grafana Loki - Manage tenant isolation
- Creating Multi-Tenant Observability Dashboards with Grafana & Loki (2025)
- Managing Grafana and Loki in a regulated multitenant environment (AWS)
HDS / RGPD / DSI hospitalière France
- Certification HDS en 2026 - ce que chaque hôpital doit vérifier (Galeon)
- HDS V2 certification framework (LSTI)
- HDS - Agence du Numérique en Santé
- Health Data Hosting (HDS) France - Microsoft Compliance
- Doctrine Numérique Santé 2025 - Règles de sécurité
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.