Saltar al contenido

Canvas bridge

Renderiza canvas y WebGL en una GPU remota real para que los píxeles que lee una página sean coherentes con la GPU que el host de render tiene de verdad; configura las cadenas de GPU de tu perfil para que coincidan con ella. Es experimental y opcional: sin --canvas-bridge-url, Clearcote renderiza todo localmente, exactamente como antes.

Por qué existe

Clearcote renderiza canvas/WebGL en la GPU que tenga realmente la máquina host. Si un perfil declara una GPU distinta a la del host, las comprobaciones estrictas que buscan navegadores antidetect o manipulación del navegador, y que comparan los píxeles renderizados con el hardware declarado, pueden notar la discrepancia: no puedes hacer que una GPU emita por software los píxeles exactos de otra. El canvas bridge elimina la discrepancia: en lugar de renderizar localmente y falsear solo la cadena de la GPU, reenvía las operaciones de canvas/WebGL a un host remoto que tiene la GPU que quieres presentar y devuelve los píxeles reales de ese host. Como reenvía las operaciones (no una biblioteca fija de imágenes pregrabadas), funciona con la mayoría de los canvas generados, no solo con pruebas conocidas.

Cómo funciona

Las APIs de lectura (getImageData, toDataURL, readPixels y measureText) devuelven los píxeles auténticos del host del bridge; en la ruta del bridge se omite el ruido de farbling local (los píxeles del bridge son la referencia real). El transporte es un WebSocket que lleva un flujo compacto de mensajes binarios. Los píxeles vienen de la GPU que tenga el navegador de render.

text
  clearcote (your automation host)         bridge host (real GPU)
  +-----------------------------+          +-------------------------+
  | page: getImageData /        |  ops --> | render browser          |
  |       toDataURL / readPixels |         | renders on the real GPU,|
  | CanvasBridgeClient  <-- pixels ---------|  reads back the pixels  |
  +-----------------------------+          +-------------------------+

Qué necesitas

  • Un host de render: cualquier máquina cuya GPU quieras presentar (una GPU de Windows para una persona de Windows). Su GPU se convierte en la identidad de canvas que presentan tus perfiles, así que elige hardware que coincida con la persona que quieres mostrar (un equipo NVIDIA para presentar NVIDIA, y así sucesivamente). Apunta el --backend cdp del servidor a un Chrome normal que ejecutes en esa máquina (recomendado), o usa --backend local con un binario de Clearcote.
  • Una ruta de red privada entre tu host de automatización y el host del bridge. El bridge habla WebSocket en texto plano (ws://): ejecútalo siempre sobre una red privada o un túnel cifrado (Tailscale, WireGuard o redirección de puertos por SSH). Nunca expongas el puerto del bridge en internet pública.

Configuración

1. Inicia el servidor de render en el host con GPU real. Es un pequeño coordinador en Python que controla un navegador headless y reproduce las operaciones reenviadas en su canvas real, así que los píxeles que devuelve son exactamente los que produce esa GPU. La GPU real del host de render se convierte en la identidad de canvas/WebGL. Con --backend cdp, al arrancar imprime la cadena del renderer de la GPU de render (render GPU='ANGLE (…)'): anótala para el paso 3. Con --backend local, lee las cadenas desde un Chrome normal en la misma máquina.

bash
pip install playwright    # one-time (no browser download needed for CDP)

# a regular Chrome on the GPU host, started with --remote-debugging-port=9222
# --user-data-dir=<a separate folder> (Chrome ignores the port on its default profile);
# its CDP URL is webSocketDebuggerUrl from http://127.0.0.1:9222/json/version
python tools/canvas-bridge-server/server.py \
    --backend cdp \
    --cdp-url ws://127.0.0.1:9222/devtools/browser/... \
    --port 8443

# Or let the server launch a Clearcote binary itself:
# python tools/canvas-bridge-server/server.py \
#     --backend local \
#     --chrome /path/to/clearcote/chrome.exe \
#     --port 8443

2. Crea un túnel. ws:// no está cifrado: pon ambos hosts en la misma red Tailscale/WireGuard, o redirige el puerto por SSH. Por defecto, el servidor escucha en localhost, así que con Tailscale/WireGuard arráncalo con --host <that interface's IP>; la ruta por SSH de abajo funciona tal cual:

bash
ssh -N -L 8443:localhost:8443 user@bridge-host
# the bridge is now reachable at ws://127.0.0.1:8443

3. Lanza el cliente (tu Clearcote de automatización) apuntando al bridge, con las cadenas de la GPU de render: el renderer exactamente como lo imprimió el servidor, y el vendor como Google Inc. (<first name in the renderer>), p. ej. Google Inc. (Intel). El bridge hace que los píxeles coincidan con la GPU de render, y estos valores hacen que también coincidan las cadenas reportadas. Sin ellos, el nombre de la GPU de la persona y los píxeles del bridge no concuerdan:

bash
--canvas-bridge-url=ws://127.0.0.1:8443 \
--no-sandbox \
--fingerprint=<seed> \
--fingerprint-gpu-vendor='Google Inc. (Intel)' \
--fingerprint-gpu-renderer='ANGLE (Intel, Intel(R) UHD Graphics ... D3D11)'
FlagSignificado
--canvas-bridge-urlEndpoint del bridge ws://host:port. Obligatorio para activar el bridge.
--canvas-bridge-authCredenciales HTTP Basic user:secret opcionales, para un despliegue que ponga autenticación delante del servidor. El servidor de referencia no las comprueba, así que confía en la red privada o el túnel para el control de acceso.
--no-sandboxObligatorio: el cliente abre el socket del bridge desde el proceso renderer, y el sandbox lo bloquea.
--fingerprintEl seed de tu persona.
--fingerprint-gpu-vendor / --fingerprint-gpu-rendererLas cadenas de la GPU de render: el renderer exactamente como lo imprimió el servidor, y el vendor como Google Inc. (<first name in the renderer>).
--canvas-bridge-modePolítica por origen: off, all (por defecto), allow o deny.
--canvas-bridge-allow / --canvas-bridge-denyListas de eTLD+1 separadas por comas que usan mode=allow o mode=deny.
--canvas-bridge-fallbackComportamiento ante un fallo de caché en frío: block (el valor por defecto) espera al bridge; local sirve píxeles locales en lugar de quedarse bloqueado.

Desde el SDK

El SDK (Node, Python y .NET; versión actual 0.31.1) expone una opción de primera clase, canvasBridge / canvas_bridge / CanvasBridge. Al definir una URL de bridge, emite los switches y añade --no-sandbox automáticamente. Para la misma política de lista de permitidos en un script de lanzamiento completo, consulta Ejemplos.

javascript
const browser = await clearcote.launch({
  fingerprint: "user-1",
  gpuVendor: "Google Inc. (Intel)",                                // "Google Inc. (<first name in the renderer>)"
  gpuRenderer: "ANGLE (Intel, Intel(R) UHD Graphics ... D3D11)",   // exactly as the server printed it
  canvasBridge: {
    url: "ws://127.0.0.1:8443",
    auth: "user:secret",
    mode: "allow",
    allow: ["example.com"],
    fallback: "local",
  },
});

Comprueba que funciona

  • El log del cliente imprime canvas-bridge: connected to <host>:<port> cuando la conexión se establece correctamente (ejecuta con --enable-logging=stderr --v=1 para verlo).
  • Carga una página que calcule el hash de una superficie canvas/WebGL: con el bridge conectado, los hashes coinciden con la GPU del host del bridge, no con la de tu host de automatización. Comprobación rápida: ejecuta el mismo canvas.toDataURL() con y sin el bridge; los resultados son distintos.
  • Si no se puede llegar al bridge, Clearcote registra una advertencia y vuelve al renderizado local: un bridge mal configurado se degrada de forma controlada y nunca rompe la página.

Advertencias & límites

  • Identidad de canvas = el host del bridge, no el seed. Todos los perfiles que comparten un host de bridge comparten el hash de canvas/WebGL de ese host, así que se pueden vincular por el hash de canvas. Para muchas identidades no vinculables, ejecuta un host de bridge (GPU) por grupo de identidades.
  • Latencia. Una lectura de píxeles que no está en la caché del bridge es un viaje de ida y vuelta por la red que bloquea (timeout de 5s y, después, fallback local), salvo que fallback sea local; el motor hace prefetch después de cada dibujo, así que las lecturas repetidas de píxeles de un canvas sin cambios no esperan. Cada llamada a measureText sigue siendo un viaje de ida y vuelta. Mantén el host del bridge en la misma LAN/centro de datos; evita el bridge en páginas sensibles a la latencia y con mucho canvas.
  • Las texturas procedurales de WebGL pasan por el bridge. Las fuentes de textura de imagen, canvas 2D, video, ImageBitmap y 3D recurren al renderizado local para ese canvas: siguen siendo correctas, pero no pasan por el bridge.
  • --no-sandbox es obligatorio en el cliente, y el transporte es texto plano: usa siempre un túnel y nunca expongas públicamente el puerto del bridge.
Experimental. En el build abierto desde v0.1.0-pre.12, y en el build con licencia. Para la referencia canónica (con la tabla completa de solución de problemas), consulta la guía del canvas bridge en GitHub. La mayoría de las configuraciones no necesitan el bridge: consulta Flags de huella digital para los controles estándar a nivel del motor.