Pular para o conteúdo

Canvas bridge

Renderize canvas e WebGL numa GPU remota real, para que os pixels que uma página lê sejam coerentes com a GPU que o host de renderização de fato tem — ajuste as strings de GPU do seu perfil para corresponderem a ela. Experimental e opt-in — sem --canvas-bridge-url, o Clearcote renderiza tudo localmente, exatamente como antes.

Por que ele existe

O Clearcote renderiza canvas/WebGL na GPU que a máquina host tiver de fato. Se um perfil declara uma GPU diferente da do host, verificações rigorosas de antidetect / de adulteração do navegador que comparam os pixels renderizados com o hardware declarado podem notar a divergência — não dá para fazer uma GPU emitir, via software, os pixels exatos de outra. O canvas bridge elimina essa divergência: em vez de renderizar localmente e falsificar só a string da GPU, ele encaminha as operações de canvas/WebGL para um host remoto que tem a GPU que você quer apresentar, e devolve os pixels reais desse host. Como o que se encaminha são as operações (e não uma biblioteca fixa de imagens pré-gravadas), ele dá conta da maioria dos canvases gerados, não só de probes conhecidos.

Como funciona

As APIs de leitura — getImageData, toDataURL, readPixels e measureText — retornam os pixels autênticos do host da ponte; o ruído local de farbling não é aplicado no caminho da ponte (os pixels da ponte são a referência real). O transporte é um WebSocket que carrega um fluxo compacto de mensagens binárias. Os pixels vêm da GPU que o navegador de renderização tiver.

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  |
  +-----------------------------+          +-------------------------+

Do que você precisa

  • Um host de renderização — qualquer máquina cuja GPU você queira apresentar (uma GPU Windows para uma persona Windows). A GPU dele passa a ser a identidade de canvas que os seus perfis apresentam, então escolha um hardware que corresponda à persona que você quer mostrar (uma máquina NVIDIA para apresentar NVIDIA, e assim por diante). Aponte o --backend cdp do servidor para um Chrome comum que você roda nessa máquina (recomendado), ou use --backend local com um binário do Clearcote.
  • Um caminho de rede privado entre o seu host de automação e o host da ponte. A ponte fala WebSocket em texto puro (ws://) — rode-a sempre numa rede privada ou num túnel criptografado (Tailscale, WireGuard ou encaminhamento de porta via SSH). Nunca exponha a porta da ponte na internet pública.

Configuração

1. Inicie o servidor de renderização no host com GPU real. É um pequeno coordenador em Python que controla um navegador headless e reproduz as operações encaminhadas no canvas real dele, então os pixels que ele devolve são exatamente o que aquela GPU produz. A GPU real do host de renderização passa a ser a identidade de canvas/WebGL. Com --backend cdp, ele imprime a string de renderer da GPU de renderização ao iniciar (render GPU='ANGLE (…)') — anote-a para o passo 3. Com --backend local, leia as strings de um Chrome comum na mesma 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. Crie um túnel. ws:// não é criptografado — coloque os dois hosts na mesma rede Tailscale/WireGuard, ou encaminhe a porta via SSH. Por padrão, o servidor escuta só em localhost; então, no Tailscale/WireGuard, inicie-o com --host <that interface's IP>. A rota por SSH abaixo funciona do jeito que está:

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

3. Inicie o cliente (o Clearcote da sua automação) apontando para a ponte, com as strings da GPU de renderização — o renderer exatamente como o servidor o imprimiu, e o vendor como Google Inc. (<first name in the renderer>), por exemplo Google Inc. (Intel). A ponte faz os pixels corresponderem à GPU de renderização, e esses valores fazem as strings reportadas corresponderem também. Sem eles, o nome da GPU da persona e os pixels vindos da ponte não batem:

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 da ponte, ws://host:port. Obrigatório para ativar a ponte.
--canvas-bridge-authCredenciais HTTP Basic user:secret opcionais, para uma implantação que coloca autenticação na frente do servidor. O servidor de referência não as verifica, então conte com a rede privada ou o túnel para o controle de acesso.
--no-sandboxObrigatório — o cliente abre o socket da ponte a partir do processo renderer, e o sandbox bloqueia isso.
--fingerprintA seed da sua persona.
--fingerprint-gpu-vendor / --fingerprint-gpu-rendererAs strings da GPU de renderização: o renderer exatamente como o servidor o imprimiu, o vendor como Google Inc. (<first name in the renderer>).
--canvas-bridge-modePolítica por origem: off, all (padrão), allow ou deny.
--canvas-bridge-allow / --canvas-bridge-denyListas de eTLD+1 separadas por vírgula, usadas por mode=allow ou mode=deny.
--canvas-bridge-fallbackComportamento em cache miss a frio: block (o padrão) espera pela ponte; local serve pixels locais em vez de travar.

Pelo SDK

O SDK (Node, Python e .NET; versão atual 0.31.1) expõe uma opção dedicada, canvasBridge / canvas_bridge / CanvasBridge. Definir uma URL de ponte emite os switches e adiciona --no-sandbox automaticamente. Para a mesma política de allow-list num script de inicialização completo, veja Exemplos.

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",
  },
});

Confirme que está funcionando

  • O log do cliente imprime canvas-bridge: connected to <host>:<port> quando a conexão dá certo (rode com --enable-logging=stderr --v=1 para ver).
  • Carregue uma página que calcula o hash de uma superfície de canvas/WebGL — com a ponte conectada, os hashes correspondem à GPU do host da ponte, não à do seu host de automação. Verificação rápida: rode o mesmo canvas.toDataURL() com e sem a ponte; os resultados são diferentes.
  • Se a ponte estiver inacessível, o Clearcote registra um aviso e volta para a renderização local — uma ponte mal configurada degrada de forma controlada e nunca quebra a página.

Ressalvas & limites

  • Identidade de canvas = o host da ponte, não a seed. Todos os perfis que compartilham um host de ponte compartilham o hash de canvas/WebGL desse host, então dá para vinculá-los pelo hash de canvas. Para muitas identidades não vinculáveis, rode um host de ponte (GPU) por grupo de identidades.
  • Latência. Uma leitura de pixels que não acha nada no cache da ponte vira uma ida e volta bloqueante pela rede (timeout de 5s, depois fallback local), a menos que fallback seja local; o motor faz prefetch depois de cada desenho, então leituras repetidas de pixels de um canvas que não mudou não esperam. Cada chamada de measureText continua sendo uma ida e volta. Mantenha o host da ponte na mesma LAN/datacenter; evite a ponte em páginas sensíveis à latência e pesadas em canvas.
  • Texturas WebGL procedurais passam pela ponte. Fontes de textura de imagem, canvas 2D, vídeo, ImageBitmap e 3D fazem aquele canvas voltar para a renderização local; assim, continuam corretas, mas fora da ponte.
  • --no-sandbox é obrigatório no cliente, e o transporte é em texto puro — use sempre um túnel; nunca exponha a porta da ponte publicamente.
Experimental. No build aberto desde v0.1.0-pre.12, e no build licenciado. Para a referência canônica (com a tabela completa de solução de problemas), veja o guia do canvas bridge no GitHub. A maioria das configurações não precisa da ponte — veja Flags de fingerprint para os controles padrão no nível do motor.