Playwright & Puppeteer
Clearcote, c’est simplement Chromium : il remplace directement le navigateur dans les outils d’automatisation que vous utilisez déjà.
La méthode SDK (recommandée)
Le package clearcote (npm, PyPI et NuGet) transforme les options d’identité en arguments nommés et renvoie des objets Playwright ordinaires. À la première utilisation, il télécharge le navigateur et en vérifie le SHA-256 — le build ouvert sans clé de licence, le dernier build sous licence avec une clé (voir Installation) — puis applique les valeurs par défaut décrites plus bas.
# pip install clearcote
from clearcote import launch
browser = launch(fingerprint="seed-123", platform="windows", brand="Chrome")
page = browser.new_page()
page.goto("https://example.com")
browser.close()SDK actuel : 0.31.1. Depuis la 0.23, launch() en Python et en Node s’exécute sur un vrai répertoire de profil jetable (supprimé à la fermeture) au lieu de la navigation privée : la surface liée au profil correspond ainsi à celle d’un vrai Chrome, et widevine: true peut charger le module DRM. Il renvoie un handle qui se comporte comme un navigateur : newPage() fonctionne comme d’habitude, mais newContext() renvoie ce même contexte de profil plutôt qu’un contexte isolé. Pour cloisonner les cookies, lancez des navigateurs distincts ; passez ephemeralProfile: false / ephemeral_profile=False pour retrouver l’ancien Browser en navigation privée, ou userDataDir / user_data_dir pour conserver le profil.
L’API asynchrone (clearcote.async_api) accepte les mêmes options de persona et de proxy dans une boucle asyncio et renvoie des objets Playwright asynchrones ; son launch() est en navigation privée, utilisez donc launch_persistent_context() pour un profil (et pour widevine=True). Le SDK .NET couvre LaunchEphemeralProfileAsync (recommandé — le LaunchAsync de .NET est en navigation privée et ne sait pas ajuster une fenêtre headless), LaunchPersistentContextAsync, ServeAsync, le téléchargement vérifié, les licences, Geoip et la simulation d’entrées humaines (des appels explicites HumanClickAsync / HumanTypeAsync / HumanSelectOptionAsync plutôt qu’un flag de lancement). Les profils enregistrés, profile: "auto", les vérifications de cohérence du rendu, Widevine et les helpers d’agent sont pour l’instant réservés à Python & Node. Pour des workflows complets à copier-coller, voir Exemples.
Puppeteer et autres clients CDP (serve)
Le SDK n’a pas de lanceur Puppeteer. À la place, serve() démarre Clearcote avec les paramètres de lancement du SDK (persona, proxy, valeurs par défaut) et un endpoint CDP en loopback, auquel n’importe quel client CDP se connecte — Puppeteer, le connectOverCDP de Playwright, browser-use, Crawl4AI, Stagehand. Cela fonctionne avec le build sous licence, et rien n’ajoute --enable-automation. humanize agit côté Playwright : il ne s’applique donc pas à un client connecté de cette façon.
import { serve } from "clearcote";
import puppeteer from "puppeteer-core";
const srv = await serve({ fingerprint: "seed-123", platform: "windows" });
const browser = await puppeteer.connect({ browserURL: srv.cdpUrl, defaultViewport: null });
const page = await browser.newPage();
await page.goto("https://example.com");
await browser.disconnect();
await srv.close();En headless, serve() donne au navigateur un écran de taille réelle et ajuste sa fenêtre à la zone de travail avant que le moindre client ne se connecte (0.31+) ; passez windowSize / window_size pour une fenêtre plus petite. Depuis un shell, clearcote serve fait la même chose sous forme de service permanent — et peut attribuer à chaque connexion son propre navigateur, avec l’identité, le proxy, le fuseau horaire et la langue tirés de l’URL de connexion. Voir Déploiement.
Piloter directement le binaire (build ouvert)
Vous pouvez aussi pointer votre propre lanceur vers le build ouvert avec executablePath (Node) ou executable_path (Python) et passer les options d’identité dans args. Le build sous licence ne démarre pas de cette façon — il a besoin du jeton de licence que le SDK récupère ; utilisez donc launch() ou serve() ci-dessus. Retirez --enable-automation comme le fait le SDK (Puppeteer et les anciennes versions de Playwright l’ajoutent ; il fait passer Chromium en mode automatisation). Aucune des valeurs par défaut du SDK (langue, politique WebRTC, géométrie de la fenêtre) ne s’applique dans ce cas.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
executable_path=r"C:\clearcote\chrome.exe",
headless=False,
ignore_default_args=["--enable-automation"],
args=[
"--fingerprint=seed-123",
"--fingerprint-platform=windows",
"--timezone=America/New_York",
],
)
page = browser.new_page()
page.goto("https://abrahamjuliot.github.io/creepjs/")
browser.close()Résoudre ou précharger le binaire vérifié
Le SDK résout le navigateur dans cet ordre : un executablePath / executable_path explicite, puis CLEARCOTE_BINARY, puis la version que vous avez demandée (ou CLEARCOTE_BROWSER_VERSION), puis le build sous licence si une clé de licence est trouvée (l’option licenseKey, CLEARCOTE_LICENSE_KEY ou ~/.clearcote/license.key), et enfin le build ouvert épinglé dans cette version du SDK. Si vous fournissez un chemin, le SDK vérifie avant le lancement qu’aucun fichier n’y manque ni n’est tronqué. Appelez download / executable_path pour préremplir le cache sans lancer le navigateur, utiliser un répertoire de cache personnalisé ou opter, à l’exécution, pour le build ouvert le plus récent publié sur GitHub.
from clearcote import download, launch
chrome = download(cache_dir=r"C:\clearcote-cache", auto_update=True)
browser = launch(executable_path=chrome, fingerprint="seed-123")Le mode épinglé vérifie les valeurs SHA-256 intégrées au SDK. autoUpdate / auto_update est à activer explicitement et vérifie le manifeste des sommes de contrôle de la version ; si gpg est disponible, il vérifie aussi le manifeste signé par rapport à l’empreinte épinglée de la clé de signature Clearcote. Il ne s’applique qu’au build ouvert : avec une clé de licence, download() récupère à la place le build sous licence actuel (choisissez-en un avec version ou releaseChannel — voir choisir un build). En .NET, DownloadAsync ne récupère que le build ouvert ; préchargez le build sous licence avec ExecutablePathAsync(new LaunchOptions { LicenseKey = … }).
Importer un profil de vrai Chrome
Au lieu de la persona synthétique dérivée de la seed, vous pouvez faire en sorte que Clearcote renvoie les valeurs capturées sur une vraie machine. Capturez un profil sur un Chrome donneur avec le collecteur situé dans tools/fingerprint-collect (ouvrez collect.html, cliquez sur « Capture » et il télécharge un profil JSON), ou partez du jeu de données open source chrome-fingerprints (10k enregistrements) avec le convertisseur fourni convert_dataset.py. La capture couvre navigator, la géométrie de l’écran, le vendor/renderer WebGL + les limites getParameter, Web Audio, les voix de synthèse vocale, les polices, les codecs et les caractéristiques CSS @media.
Passez le profil au SDK sous forme de chemin de fichier, d’objet ou de chaîne JSON — il se charge pour vous de l’empaquetage gzip + base64. Les champs présents dans le profil remplacent ce que le navigateur renverrait sinon ; les champs absents retombent sur les valeurs par défaut du navigateur. Le SDK déduit aussi Accept-Language du navigator.languages du profil si vous n’en définissez pas explicitement. Les deux builds lisent les profils importés ; sur le build sous licence, ils s’appliquent intégralement à partir de la 151 r19.
Ne passez pas de seed fingerprint en plus d’un profil. La seed active la couche de farbling, qu’un scoring strict interprète comme une manipulation du canvas, et elle ne vous apporte rien que le profil ne fournisse déjà. Utilisez l’un ou l’autre. Un profil ne remplace que les valeurs renvoyées — il ne change jamais ce qui produit vos pixels : deux comptes sur deux profils différents, sur la même machine, produisent donc toujours un canvas identique. Si vous avez besoin de séparer le rendu par compte, utilisez une seed par compte et aucun profil.
from clearcote import launch
browser = launch(fingerprint_profile="profile.json", disable_gpu_fingerprint=True, fingerprint_noise=False)
page = browser.new_page()
page.goto("https://example.com")
browser.close()Cohérent par défaut (sans flag supplémentaire)
Quelles que soient vos options de lancement, le moteur maintient les surfaces secondaires en accord avec la persona choisie — pour une persona Windows, avec ce que renvoie un vrai Chrome sur un PC de bureau Windows. La plateforme de la persona correspond par défaut à l’OS de votre hôte : sur un hôte Linux, ces surfaces suivent donc une persona Linux, sauf si vous passez platform. Les limites WebGL getParameter (WebGL1 + WebGL2) renvoient les valeurs de la persona, plafonnées à ce que le GPU de cette machine peut réellement fournir, et UNMASKED_RENDERER / UNMASKED_VENDOR sont constants sur toute la session (le même GPU sur tous les sites, qui suit la persona). Pour une persona Windows, navigator.getBattery() renvoie un PC de bureau sur secteur, navigator.connection une connexion résidentielle, et AudioContext la fréquence d’échantillonnage et la latence Windows-WASAPI correspondantes. getScreenDetails() renvoie un seul écran, et @media (pointer: fine) / (hover: hover) correspondent à un PC de bureau avec une souris.
Vérifié sur la 153 r26, en mode headed sous Windows (septembre 2026) : BrowserScan ne signale aucun bot et CreepJS affiche un score stealth de 0 %. Derrière un proxy, ajoutez geoip pour que le fuseau horaire et WebRTC correspondent à la sortie du proxy.
Événements console et erreurs de page
Le moteur ne transmet pas les événements de console ni d’erreur de page aux clients d’automatisation : page.on("console") et page.on("pageerror") ne reçoivent donc rien. C’est voulu — les transmettre est précisément ce que mesure une sonde de détection de l’automatisation. Dans la page, les handlers window.onerror et unhandledrejection se déclenchent normalement : pour capturer la sortie console, collectez-la dans la page et relisez-la avec page.evaluate(). Au lancement, les SDK Python et Node affichent une note à ce sujet (une seule fois).
Adapter automatiquement la région au proxy (geoip)
Passez geoip avec un proxy et le SDK résout l’IP de sortie du proxy — en la cherchant dans la base hors ligne geoip-all-in-one — puis définit pour cette région un ensemble cohérent fuseau horaire + langue principale de navigator + Accept-Language + IP WebRTC. Plus besoin d’associer à la main un fuseau horaire à chaque proxy :
from clearcote import launch
browser = launch(
fingerprint="user-7423",
proxy={"server": "http://host:8080", "username": "u", "password": "p"},
geoip=True, # timezone + language auto-matched to the proxy's region
)Il définit aussi la géolocalisation et la liste complète navigator.languages de la région, fonctionne avec les proxys HTTP et SOCKS5 (identifiants compris) et existe dans les trois SDK (Geoip = true en .NET). La première exécution télécharge la base de données (environ 50 MB). Si la région ne peut pas être résolue, le lancement s’arrête avec une GeoipError au lieu de démarrer discrètement avec l’horloge et la langue de cette machine ; définissez à la fois timezone et acceptLanguage pour lancer quand même. La recherche dispose de 20 secondes (CLEARCOTE_GEOIP_TIMEOUT_SECONDS) ; en .NET, l’erreur est GeoipException. Sans geoip ni timezone explicite, le fuseau horaire suit la langue — en-US signifie New York, même derrière un proxy allemand.
Vous préférez le définir vous-même ? Utilisez acceptLanguage (Node) / accept_language (Python), par exemple "en-US,en" — il définit l’en-tête Accept-Language, le tableau complet navigator.languages et navigator.language — et Intl / toLocaleString le suivent également.
Entrées humanisées (humanize & showCursor)
Passez humanize et toutes les entrées — déplacements, clics, glisser-déposer, défilement et frappe — suivent un même standard humanisé, aussi bien au niveau de la page (page.click / hover / type / fill / mouse.* / keyboard.type) qu’au niveau des locators (locator.click / type / fill / pressSequentially / dragTo / …). Les déplacements suivent une courbe de Bézier cubique légèrement incurvée, construite à partir de la dernière position du curseur et parcourue comme une somme de sous-mouvements à jerk minimal (un mouvement balistique principal + un mouvement correctif — la vitesse à plusieurs pics d’un vrai geste de pointage, et non une seule cloche symétrique), et tout est envoyé sous forme d’événements réels et de confiance (isTrusted === true, et navigator.webdriver reste à false). Sur Pro, les clics par coordonnées (mouse.click(x, y)) suivent des mouvements enregistrés sur de vraies personnes ; tout le reste, ainsi que chaque clic sur le build ouvert et sur l’offre Gratuit avec GitHub, utilise des trajectoires générées. Ajoutez showCursor pour dessiner un point qui suit le mouvement et le voir à l’œuvre.
Comme les déplacements passent par des entrées natives, un bouton enfoncé avec mouse.down() reste maintenu pendant le déplacement — down → move → up donne donc un vrai glisser avec bouton maintenu (les contrôles qu’on positionne en glissant, comme les sliders, reçoivent un glisser réellement appuyé), et locator.dragTo est lui aussi humanisé. La frappe se fait touche par touche, avec un délai aléatoire entre les touches + des pauses entre les mots et, de temps en temps, une faute de frappe corrigée ; le défilement utilise une inertie ease-out avec, de temps en temps, une pause de lecture. fill donne le focus au champ et tape la valeur (au-delà d’environ 200 caractères, les valeurs restent atomiques pour que les remplissages en masse ne s’éternisent pas).
from clearcote import launch
browser = launch(fingerprint="seed-123", humanize=True, show_cursor=True)
page = browser.new_page()
page.goto("https://example.com")
page.click("text=Sign in") # eased curve, then a trusted click
page.fill("#email", "you@example.com") # focus + key-by-key human typing
page.locator("#password").type("s3cr3t") # locators are humanized too
# held-button drag (e.g. a slider): the press stays held across the move
x0, y0, x1 = 100, 300, 400 # the handle's start, and where to release it
page.mouse.move(x0, y0); page.mouse.down()
page.mouse.move(x1, y0); page.mouse.up()
browser.close()Vérifier la cohérence du backend de rendu (checkRenderCoherence)
Une persona peut annoncer un GPU, mais si la page est en réalité rendue par un rasterizer logiciel (SwiftShader / llvmpipe — courant en headless sans GPU), un détecteur strict peut s’en apercevoir. Sondez une page active : la vérification lit le vendor/renderer WebGL (non masqué) que la page voit réellement, signale un repli sur un rasterizer logiciel (un indice headless rédhibitoire — activez le canvas bridge ou lancez en mode headed sur un vrai GPU) ou une paire vendor/renderer incohérente, et renvoie un verdict structuré. Passez le GPU annoncé pour contrôler aussi la famille effectivement rendue. Disponible en synchrone, en asynchrone et en Node.
from clearcote import launch, check_render_coherence
browser = launch(fingerprint="seed-123")
page = browser.new_page(); page.goto("about:blank")
verdict = check_render_coherence(page) # {'renderer', 'software_suspected', 'coherent', 'warnings'}
if not verdict["coherent"]:
print(verdict["warnings"]) # e.g. software rasterizer / incoherent GPU family
browser.close()Profils & persistance
Conservez une identité stable d’une exécution à l’autre en réutilisant la même seed fingerprint, et faites persister cookies/stockage avec un répertoire user-data :
from clearcote import launch_persistent_context
ctx = launch_persistent_context(r"C:\clearcote\profiles\acme", fingerprint="acme-tenant-7", headless=False)
page = ctx.pages[0] if ctx.pages else ctx.new_page()Les cookies d’un répertoire de profil sont chiffrés avec une clé liée à la machine qui l’a créé. Pour copier un profil sur une autre machine sans perdre ses cookies, passez portableProfile: true / portable_profile=True (la clé voyage avec le profil) ou encryptionKey / encryption_key (la clé est dérivée de votre secret, et rien de sensible n’est écrit sur le disque). Build sous licence, Python & Node.
Le SDK gère aussi des personas enregistrées : un Profile stocke les options d’empreinte, les paramètres de proxy, les paramètres du canvas bridge et d’autres options de lancement au format JSON dans ~/.clearcote/profiles (modifiable via CLEARCOTE_PROFILE_DIR).
from clearcote import Profile, launch, launch_persistent_context
Profile("acct-1", {
"fingerprint": "acct-1",
"gpu_vendor": "Google Inc. (Intel)",
"gpu_renderer": "ANGLE (Intel, Intel(R) UHD Graphics ... D3D11)",
"canvas_bridge": {"url": "ws://127.0.0.1:8443", "auth": "user:secret"},
}).save()
ctx = launch_persistent_context(r"C:\clearcote\profiles\acct-1", profile="acct-1")
browser = launch(profile="acct-1", headless=False)Les profils enregistrés sont stockés en clair et peuvent contenir des identifiants comme canvasBridge.auth. Traitez les fichiers de profil comme des entrées de confiance, et ne les commitez ni ne les partagez.Autres options de lancement
extensions— une liste de chemins de répertoires d’extensions non empaquetées (génère--load-extension+--disable-extensions-except).disablePrivacySandbox/disable_privacy_sandbox— metteztruepour désactiver les API Privacy Sandbox (Topics, FLEDGE / Protected Audience, Shared Storage, Private Aggregation, Fenced Frames). Option désactivée par défaut depuis la 0.23 : la persona par défaut se présente comme Google Chrome, qui les embarque toutes. Ne l’activez que si la persona est un Chromium dégooglisé. WebUSB n’est pas concerné.agentTyping/agent_typing— la cadence de frappe de l’agent (humanpar défaut /fast/instant). Voir Agent.tlsProfile— maintient le ClientHello TLS cohérent avec la version de Chrome annoncée par la persona, pour que la couche réseau suive l’UA (et non le TLS natif du build). La valeur par défaut,"match-persona", suitbrandVersion;"native"n’y touche pas ;"chrome-<major>"épingle une version majeure. Voir Flags d’empreinte.platform: "android"— une persona mobile en best effort (tactile, pointeur coarse, écran/DPR mobile, WebGL Mali/Adreno, viewport de téléphone). Sur un moteur desktop, le rendu GPU reste celui d’un desktop — associez-le au canvas bridge pour la cohérence du rendu.storageQuota,fingerprintProfile,canvasBridge,webrtcIp,acceptLanguage,disableGpuFingerprint,fingerprintNoise— voir Flags d’empreinte.
Options récentes (build sous licence)
Elles nécessitent le build sous licence (Gratuit avec GitHub ou Pro). La révision du moteur requise pour chacune figure entre crochets ; sur un moteur plus ancien, le SDK écarte celles de la 152 r22 avec un avertissement, et le moteur ignore les autres.
allowThirdPartyCookies/allow_third_party_cookies— autorise les cookies tiers, comme le fait Chrome d’origine. La base dégooglisée les bloque par défaut, ce qui casse les frames intégrées de connexion, de paiement et de challenge qui en dépendent. [152 r22]transparentProxy/transparent_proxy— masque le proxy dans les en-têtes des requêtes et dans le timing des connexions (les requêtes HTTP en clair ne portent aucun en-tête de proxy ; les connexions via le proxy présentent le timing d’une connexion réutilisée). Nécessite un proxy. [152 r22]fingerprintVoices: false/fingerprint_voices=False— conserve les voix de synthèse de cette machine au lieu de la liste de la persona. [152 r22]fingerprint: "off"— lance le navigateur sans aucune persona, pour le dépannage. [152 r22]socks5Udp/socks5_udp— fait passer l’UDP de WebRTC par un proxysocks5://: la voix, la vidéo et les connexions pair à pair fonctionnent tout en sortant par l’adresse du proxy. Le proxy doit l’autoriser ; beaucoup de pools résidentiels ne le font pas. [151 r17]portableProfile/encryptionKey— des profils copiables d’une machine à l’autre (voir plus haut). [151 r14 ; Python & Node]personaSchema: 2/persona_schema=2— un modèle d’identité optionnel dans lequel l’écran et la puce graphique sont assortis au processeur et à la mémoire qu’annonce la persona. Désactivé par défaut, pour que chaque seed existante conserve son identité. N’ajoutezrealGpuHost/real_gpu_hostque sur une machine dotée d’une vraie carte graphique. [151 r19 ; Python & Node]shaderDialect: "hlsl"— voir Dialecte de shader. [151 r15]profile: "auto"— lance une vraie empreinte capturée, choisie pour cette machine, au lieu d’une seed ; ajustez le choix avecprofileSelect/profile_select. Python & Node. En Node, cela fonctionne avec lelaunch()par défaut,launchPersistentContext()etserve()à partir du SDK 0.31.1 ; les versions 0.31.0 et antérieures exigeaientephemeralProfile: falseet échouaient avecserve().
Non liées à un build du moteur :
version/releaseChannel— choisit un build ; voir choisir un build.licenseKey/license_key/LicenseKey— la clé de licence, si elle ne se trouve ni dansCLEARCOTE_LICENSE_KEYni dans~/.clearcote/license.key.licenseThroughProxy/license_through_proxy(ouCLEARCOTE_LICENSE_THROUGH_PROXY=1) — envoie les appels de licence via le proxy du lancement plutôt que depuis cette machine.ephemeralProfile/userDataDirsurlaunch()— voir la remarque sous La méthode SDK plus haut.widevine: true— lecture DRM (les deux builds) ; voir Widevine & DRM.quiet— masque les avertissements de lancement et l’affichage de la progression du SDK.
Valeurs par défaut cohérentes (modifiables)
Le SDK applique quelques valeurs par défaut pensées pour la furtivité, afin de ne pas laisser passer les indices évidents :
- En mode headed, aucun viewport n’est émulé par défaut (
viewport: null/no_viewport=True), pour quewindow.innerWidthsuive la vraie fenêtre de l’OS — un viewport émulé de 1280×720 posé sur une vraie fenêtre trahit une fenêtre impossible. Passez unviewportexplicite pour changer ce comportement. - WebRTC utilise par défaut
disable_non_proxied_udp: aucun trafic UDP ne sort en dehors du proxy et l’adresse de votre machine reste privée. Seule votre propre--webrtc-ip-handling-policydansargsprend le dessus — ainsi que, sur le build ouvert,webrtcIp/geoip, qui laissent de nouveau l’UDP WebRTC sortir par votre propre connexion (le build sous licence bloque l’UDP WebRTC dans tous les cas). Avec cette politique et sanswebrtcIp, une page n’obtient aucun candidat ICE — derrière un proxy, passezgeoip(ouwebrtcIp) pour que WebRTC renvoie l’adresse du proxy, ousocks5Udppour faire passer du vrai UDP par un proxy SOCKS5. - Derrière un proxy, QUIC / HTTP-3 est désactivé, comme pour un vrai Chrome derrière un proxy — aucun trafic UDP n’est donc tenté en dehors de celui-ci.
- Par défaut, la plateforme de la persona est celle de votre OS hôte, et la marque est Google Chrome. Sans
timezonenigeoip, le fuseau horaire suit la langue (en-US→ New York). - Les identifiants du proxy sont transmis au navigateur, et non à Playwright, là où le moteur le permet : toujours en SOCKS5 (Playwright ne sait pas du tout s’authentifier en SOCKS5), en HTTP(S) sur la 151 r19+ pour que le cache des pages reste actif (Python & Node). Voir ci-dessous.
humanizeeffectue une vérification préalable d’actionnabilité avant chaque clic de confiance (visible / activé / stable + un contrôle de recouvrement viaelementFromPoint) et se rabat sur le clic natif : un clic de confiance ne se déclenche ainsi jamais sous un overlay ni en pleine animation.
SOCKS5 avec identifiants
Chromium d’origine ne sait pas du tout s’authentifier auprès d’un proxy SOCKS5 — il n’implémente pas la sous-négociation nom d’utilisateur/mot de passe —, d’où la solution habituelle : un relais local qui détient les identifiants. Le build sous licence l’implémente dans le moteur (RFC 1929) : aucun relais n’est nécessaire. Passez le nom d’utilisateur et le mot de passe dans des champs séparés ou dans l’adresse (socks5://user:pass@host:port) ; dans les deux cas, le SDK les transmet au moteur. Les identifiants dans l’adresse nécessitent le SDK 0.31.1 ou plus récent : les SDK plus anciens laissaient Playwright les abandonner, et le proxy ne recevait aucun identifiant.
from clearcote import launch_persistent_context
ctx = launch_persistent_context(
"./profile",
proxy={"server": "socks5://proxy.example.net:1080", "username": "user", "password": "pass"},
)
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://api.ipify.org?format=json") # confirm the exit IP is the proxy'sNécessite le build sous licence (Gratuit avec GitHub ou Pro, moteur 151 r14 ou plus récent) ; le build ouvert ne sait pas s’authentifier auprès d’un proxy SOCKS5, utilisez donc avec lui un relais local ou un proxy HTTP.
Vérifiez toujours l’adresse de sortie avant de faire confiance à une session. Un proxy qui tombe en panne sans rien dire et laisse passer le trafic l’envoie depuis votre propre IP, et toutes les autres précautions deviennent vaines. Vérifiez-la une fois au lancement auprès d’un service qui renvoie l’adresse vue de l’extérieur, plutôt que de la tenir pour acquise.
Astuce : dérivez la seed de votre propre identifiant de compte/tenant pour que chaque identité soit reproductible — même seed, même empreinte de navigateur, à chaque fois. La liste complète des options se trouve dans Flags d’empreinte.