# Scrapfly Documentation

## Table of Contents

### Dashboard

- [Intro](https://scrapfly.io/docs)
- [Project](https://scrapfly.io/docs/project)
- [Account](https://scrapfly.io/docs/account)
- [Workspace & Team](https://scrapfly.io/docs/workspace-and-team)
- [Billing](https://scrapfly.io/docs/billing)

### Products

#### MCP Server

- [Getting Started](https://scrapfly.io/docs/mcp/getting-started)
- [Tools & API Spec](https://scrapfly.io/docs/mcp/tools)
- [Authentication](https://scrapfly.io/docs/mcp/authentication)
- [Examples & Use Cases](https://scrapfly.io/docs/mcp/examples)
- [FAQ](https://scrapfly.io/docs/mcp/faq)
##### Integrations

- [Overview](https://scrapfly.io/docs/mcp/integrations)
- [Claude Desktop](https://scrapfly.io/docs/mcp/integrations/claude-desktop)
- [Claude Code](https://scrapfly.io/docs/mcp/integrations/claude-code)
- [ChatGPT](https://scrapfly.io/docs/mcp/integrations/chatgpt)
- [Cursor](https://scrapfly.io/docs/mcp/integrations/cursor)
- [Cline](https://scrapfly.io/docs/mcp/integrations/cline)
- [Windsurf](https://scrapfly.io/docs/mcp/integrations/windsurf)
- [Zed](https://scrapfly.io/docs/mcp/integrations/zed)
- [Roo Code](https://scrapfly.io/docs/mcp/integrations/roo-code)
- [VS Code](https://scrapfly.io/docs/mcp/integrations/vscode)
- [LangChain](https://scrapfly.io/docs/mcp/integrations/langchain)
- [LlamaIndex](https://scrapfly.io/docs/mcp/integrations/llamaindex)
- [CrewAI](https://scrapfly.io/docs/mcp/integrations/crewai)
- [OpenAI](https://scrapfly.io/docs/mcp/integrations/openai)
- [n8n](https://scrapfly.io/docs/mcp/integrations/n8n)
- [Make](https://scrapfly.io/docs/mcp/integrations/make)
- [Zapier](https://scrapfly.io/docs/mcp/integrations/zapier)
- [Vapi AI](https://scrapfly.io/docs/mcp/integrations/vapi)
- [Agent Builder](https://scrapfly.io/docs/mcp/integrations/agent-builder)
- [Custom Client](https://scrapfly.io/docs/mcp/integrations/custom-client)


#### Web Scraping API

- [Getting Started](https://scrapfly.io/docs/scrape-api/getting-started)
- [API Specification]()
- [Monitoring](https://scrapfly.io/docs/monitoring)
- [Customize Request](https://scrapfly.io/docs/scrape-api/custom)
- [Debug](https://scrapfly.io/docs/scrape-api/debug)
- [Anti Scraping Protection](https://scrapfly.io/docs/scrape-api/anti-scraping-protection)
- [Proxy](https://scrapfly.io/docs/scrape-api/proxy)
- [Proxy Mode](https://scrapfly.io/docs/scrape-api/proxy-mode)
- [Proxy Mode - Screaming Frog](https://scrapfly.io/docs/scrape-api/proxy-mode/screaming-frog)
- [Proxy Mode - Apify](https://scrapfly.io/docs/scrape-api/proxy-mode/apify)
- [(Auto) Data Extraction](https://scrapfly.io/docs/scrape-api/extraction)
- [Javascript Rendering](https://scrapfly.io/docs/scrape-api/javascript-rendering)
- [Javascript Scenario](https://scrapfly.io/docs/scrape-api/javascript-scenario)
- [SSL](https://scrapfly.io/docs/scrape-api/ssl)
- [DNS](https://scrapfly.io/docs/scrape-api/dns)
- [Cache](https://scrapfly.io/docs/scrape-api/cache)
- [Batch (Multi-URL Scraping)](https://scrapfly.io/docs/scrape-api/batch)
- [Session](https://scrapfly.io/docs/scrape-api/session)
- [Webhook](https://scrapfly.io/docs/scrape-api/webhook)
- [Schedule](https://scrapfly.io/docs/scrape-api/schedule)
- [Screenshot](https://scrapfly.io/docs/scrape-api/screenshot)
- [Errors](https://scrapfly.io/docs/scrape-api/errors)
- [Timeout](https://scrapfly.io/docs/scrape-api/understand-timeout)
- [Throttling](https://scrapfly.io/docs/throttling)
- [Troubleshoot](https://scrapfly.io/docs/scrape-api/troubleshoot)
- [Billing](https://scrapfly.io/docs/scrape-api/billing)
- [FAQ](https://scrapfly.io/docs/scrape-api/faq)

#### Crawler API

- [Getting Started](https://scrapfly.io/docs/crawler-api/getting-started)
- [API Specification]()
- [Retrieving Results](https://scrapfly.io/docs/crawler-api/results)
- [WARC Format](https://scrapfly.io/docs/crawler-api/warc-format)
- [Data Extraction](https://scrapfly.io/docs/crawler-api/extraction-rules)
- [Webhook](https://scrapfly.io/docs/crawler-api/webhook)
- [Schedule](https://scrapfly.io/docs/crawler-api/schedule)
- [Billing](https://scrapfly.io/docs/crawler-api/billing)
- [Errors](https://scrapfly.io/docs/crawler-api/errors)
- [Troubleshoot](https://scrapfly.io/docs/crawler-api/troubleshoot)
- [FAQ](https://scrapfly.io/docs/crawler-api/faq)

#### Screenshot API

- [Getting Started](https://scrapfly.io/docs/screenshot-api/getting-started)
- [API Specification]()
- [Accessibility Testing](https://scrapfly.io/docs/screenshot-api/accessibility)
- [Webhook](https://scrapfly.io/docs/screenshot-api/webhook)
- [Schedule](https://scrapfly.io/docs/screenshot-api/schedule)
- [Billing](https://scrapfly.io/docs/screenshot-api/billing)
- [Errors](https://scrapfly.io/docs/screenshot-api/errors)

#### Extraction API

- [Getting Started](https://scrapfly.io/docs/extraction-api/getting-started)
- [API Specification]()
- [Rules Template](https://scrapfly.io/docs/extraction-api/rules-and-template)
- [LLM Extraction](https://scrapfly.io/docs/extraction-api/llm-prompt)
- [AI Auto Extraction](https://scrapfly.io/docs/extraction-api/automatic-ai)
- [Webhook](https://scrapfly.io/docs/extraction-api/webhook)
- [Billing](https://scrapfly.io/docs/extraction-api/billing)
- [Errors](https://scrapfly.io/docs/extraction-api/errors)
- [FAQ](https://scrapfly.io/docs/extraction-api/faq)

#### Data API


#### Proxy Saver

- [Getting Started](https://scrapfly.io/docs/proxy-saver/getting-started)
- [Fingerprints](https://scrapfly.io/docs/proxy-saver/fingerprints)
- [Optimizations](https://scrapfly.io/docs/proxy-saver/optimizations)
- [SSL Certificates](https://scrapfly.io/docs/proxy-saver/certificates)
- [Protocols](https://scrapfly.io/docs/proxy-saver/protocols)
- [Pacfile](https://scrapfly.io/docs/proxy-saver/pacfile)
- [Secure Credentials](https://scrapfly.io/docs/proxy-saver/security)
- [Billing](https://scrapfly.io/docs/proxy-saver/billing)

#### Cloud Browser API

- [Getting Started](https://scrapfly.io/docs/cloud-browser-api/getting-started)
- [Proxy & Geo-Targeting](https://scrapfly.io/docs/cloud-browser-api/proxy)
- [Unblock API](https://scrapfly.io/docs/cloud-browser-api/unblock)
- [Captcha Solver](https://scrapfly.io/docs/cloud-browser-api/captcha-solver)
- [File Downloads](https://scrapfly.io/docs/cloud-browser-api/file-downloads)
- [Session Resume](https://scrapfly.io/docs/cloud-browser-api/session-resume)
- [Human-in-the-Loop](https://scrapfly.io/docs/cloud-browser-api/human-in-the-loop)
- [Debug Mode](https://scrapfly.io/docs/cloud-browser-api/debug-mode)
- [Browser Extensions](https://scrapfly.io/docs/cloud-browser-api/extensions)
- [Native Browser MCP](https://scrapfly.io/docs/cloud-browser-api/mcp)
- [DevTools Protocol](https://scrapfly.io/docs/cloud-browser-api/cdp-reference)
##### Integrations

- [Puppeteer](https://scrapfly.io/docs/cloud-browser-api/puppeteer)
- [Playwright](https://scrapfly.io/docs/cloud-browser-api/playwright)
- [Selenium](https://scrapfly.io/docs/cloud-browser-api/selenium)
- [Vercel Agent Browser](https://scrapfly.io/docs/cloud-browser-api/agent-browser)
- [Browser Use](https://scrapfly.io/docs/cloud-browser-api/browser-use)
- [Stagehand](https://scrapfly.io/docs/cloud-browser-api/stagehand)

- [Billing](https://scrapfly.io/docs/cloud-browser-api/billing)
- [Errors](https://scrapfly.io/docs/cloud-browser-api/errors)


### Tools

- [Antibot Detector](https://scrapfly.io/docs/tools/antibot-detector)

### SDK

- [Golang](https://scrapfly.io/docs/sdk/golang)
- [Python](https://scrapfly.io/docs/sdk/python)
- [Rust](https://scrapfly.io/docs/sdk/rust)
- [TypeScript](https://scrapfly.io/docs/sdk/typescript)
- [Scrapy](https://scrapfly.io/docs/sdk/scrapy)

### Integrations

- [Getting Started](https://scrapfly.io/docs/integration/getting-started)
- [LangChain](https://scrapfly.io/docs/integration/langchain)
- [LlamaIndex](https://scrapfly.io/docs/integration/llamaindex)
- [CrewAI](https://scrapfly.io/docs/integration/crewai)
- [Zapier](https://scrapfly.io/docs/integration/zapier)
- [Make](https://scrapfly.io/docs/integration/make)
- [n8n](https://scrapfly.io/docs/integration/n8n)

### Academy

- [Overview](https://scrapfly.io/academy)
- [Web Scraping Overview](https://scrapfly.io/academy/scraping-overview)
- [Tools](https://scrapfly.io/academy/tools-overview)
- [Reverse Engineering](https://scrapfly.io/academy/reverse-engineering)
- [Static Scraping](https://scrapfly.io/academy/static-scraping)
- [HTML Parsing](https://scrapfly.io/academy/html-parsing)
- [Dynamic Scraping](https://scrapfly.io/academy/dynamic-scraping)
- [Hidden API Scraping](https://scrapfly.io/academy/hidden-api-scraping)
- [Headless Browsers](https://scrapfly.io/academy/headless-browsers)
- [Hidden Web Data](https://scrapfly.io/academy/hidden-web-data)
- [JSON Parsing](https://scrapfly.io/academy/json-parsing)
- [Data Processing](https://scrapfly.io/academy/data-processing)
- [Scaling](https://scrapfly.io/academy/scaling)
- [Walkthrough Summary](https://scrapfly.io/academy/walkthrough-summary)
- [Scraper Blocking](https://scrapfly.io/academy/scraper-blocking)
- [Proxies](https://scrapfly.io/academy/proxies)

---

# Cloud Browser Live VNC Control

 1. [Cloud Browser](https://scrapfly.io/docs/cloud-browser-api/getting-started)
2. Live VNC Control

  Take live control of a running Cloud Browser session with your mouse and keyboard. Turn VNC on for the session, choose a password, share it with whoever needs to attach.

 **You own the password.** The VNC password is set by you on the connect URL and used to authenticate any client that attaches. Scrapfly does not store it, log it, or show it back to you.

## Enabling VNC on a Session

 Add `enable_vnc=true` and `vnc_password` as query parameters on the WebSocket connect URL:

 ```
wss://browser.scrapfly.io/?key=YOUR_API_KEY
                &enable_vnc=true
                &vnc_password=YOUR_VNC_PASSWORD
                &proxy_pool=public_datacenter_pool
                &os=linux
```

### Parameters

 | Parameter | Type | Required | Description |
|---|---|---|---|
| `enable_vnc` | bool | Yes | Turn on live VNC control for this session. |
| `vnc_password` | string | Conditional | Password the VNC client must supply when it connects. 6 to 128 characters. We recommend 16 random characters or more. Required when `hitl_allowed_networks` is empty; optional when an allow-list is set. |
| `hitl_allowed_networks` | string | No | Comma-separated list of IPs or CIDRs trusted to attach without a password. Empty or absent means a password is required for every attach. **Source IPs in the list skip the password check; everyone else still authenticates.** |

 Each Cloud Browser response also returns an `X-Browser-Project-Salt` header on a successful WebSocket upgrade. The value is an opaque 8-character identifier scoped to your project, stable across sessions, safe to log. See the [project salt section](https://scrapfly.io/docs/cloud-browser-api/human-in-the-loop#project-salt) for SDK helpers.

## Watching from the Dashboard

 The simplest path is the dashboard. Once your session is running, open [Cloud Browser sessions](https://scrapfly.io/dashboard/cloud-browser/sessions), find the session in the list, and click the VNC icon. A new tab opens with the live view. Paste your password when prompted.

## Using Your Own VNC Client

 Open this URL in any VNC client that speaks RFB. The run ID is the username, and the password you type is `{salt}-{vnc_password}` (see the callout below):

 ```
vnc://{run_id}@browser.scrapfly.io:5901
```

 **The password you type in your VNC client is NOT the bare `vnc_password`** you set at allocation. Scrapfly prefixes it server-side with your project salt so two customers using the same password never collide on the wire. Your client must send the salted form: ```
{project_salt}-{vnc_password}
```

 The project salt is the 8-character value returned in the `X-Browser-Project-Salt` response header at session allocation. It is deterministic per api key, so the Cloud Browser API Player and every SDK can compute it without an extra network call (see [project salt section](https://scrapfly.io/docs/cloud-browser-api/human-in-the-loop#project-salt)). Example: salt = `06f679de`, your `vnc_password` = `hunter2`, then the VNC client must type `06f679de-hunter2`.

  macOS  Windows  Linux

**Built-in: Screen Sharing** (no install needed).

1. Open Finder. Press Cmd+K (Go &gt; Connect to Server).
2. Paste `vnc://{run_id}@browser.scrapfly.io:5901`.
3. Click **Connect**. Enter `{project_salt}-{vnc_password}` at the prompt (see the callout above).

**Alternative: RealVNC Viewer** ([download](https://www.realvnc.com/en/connect/download/viewer/macos/)). Paste the same URL in the address bar.

**RealVNC Viewer** ([download](https://www.realvnc.com/en/connect/download/viewer/windows/)):

1. Launch RealVNC Viewer.
2. Paste `browser.scrapfly.io:5901` in the address bar and press Enter.
3. When prompted for credentials, enter your `{run_id}` as the username and `{project_salt}-{vnc_password}` as the password.

**Alternative: TigerVNC** ([download](https://github.com/TigerVNC/tigervnc/releases)). Same connection details; supports the `vnc://` URL scheme directly.

**Remmina** (ships with most distros, or `sudo apt install remmina remmina-plugin-vnc` on Debian/Ubuntu):

1. Open Remmina. Click the **+** button to create a new connection.
2. Protocol: **VNC**. Server: `browser.scrapfly.io:5901`. User name: `{run_id}`. Password: `{project_salt}-{vnc_password}`.
3. Click **Connect**.

**Alternative: TigerVNC**:

 ```
vncviewer browser.scrapfly.io::5901
```

TigerVNC prompts for the password on its own. Enter `{project_salt}-{vnc_password}`.

 **Important:** each active session must have a unique `vnc_password`. Scrapfly identifies which session you are attaching to by matching the password you type, so an allocation request that reuses a password from another currently-active session is rejected at creation time with [`ERR::BROWSER::CONFIG_ERROR`](https://scrapfly.io/docs/cloud-browser-api/error/ERR::BROWSER::CONFIG_ERROR). Rotate the password (a random 16+ character string is recommended) and retry.

### CLI quick reference

 Copy-paste examples for the most popular VNC clients. Replace `{run_id}`, `{project_salt}`, `{vnc_password}` with your values. The combined password the client sends is `{project_salt}-{vnc_password}`.

#### TigerVNC (Linux, macOS, Windows)

 ```
# Pre-fill the password via a passwd file (one-time):
mkdir -p ~/.vnc
printf '%s\n' '{project_salt}-{vnc_password}' | vncpasswd -f > ~/.vnc/scrapfly-passwd
chmod 600 ~/.vnc/scrapfly-passwd

# Connect (RFB 3.8, VeNCrypt-X509Plain auto-negotiates):
vncviewer \
  -SecurityTypes=VeNCrypt,VncAuth \
  -X509CA= \
  -PasswordFile=~/.vnc/scrapfly-passwd \
  -Username={run_id} \
  browser.scrapfly.io::5901
```

 TigerVNC uses double-colon (`host::5901`) for raw RFB ports. The `-X509CA=` empty value disables CA verification — the server presents a certificate signed by Scrapfly's internal CA which most systems don't trust by default. Drop `-PasswordFile` if you'd rather type the password interactively.

#### macOS Screen Sharing (URL-based)

 ```
# Opens Apple's built-in VNC client; password prompt appears in the window:
open 'vnc://{run_id}@browser.scrapfly.io:5901'
```

 Screen Sharing speaks RFB 3.3 only and does not support VeNCrypt. The mux auto-detects the older protocol and falls back to VncAuth on the wire. Connections are unencrypted on this path — use TigerVNC for TLS.

#### RealVNC Viewer (CLI)

 ```
# Connect with password on stdin (no GUI prompt):
echo '{project_salt}-{vnc_password}' | vncviewer \
  --Username={run_id} \
  --UseLocalCursor=true \
  --SendKeyEvents=true \
  browser.scrapfly.io:5901
```

 RealVNC Viewer's address bar does NOT accept the `vnc://` scheme — pass bare `host:port` only. On first connect it shows a "trust this certificate?" dialog; click **Continue**. For unattended use add `--VerifySecurityFingerprint=false` (less secure but skips the prompt).

#### Remmina (Linux GUI / CLI)

 ```
# One-shot CLI invocation:
remmina -c vnc://{run_id}:{project_salt}-{vnc_password}@browser.scrapfly.io:5901
```

 Remmina supports `vnc://user:pass@host:port` URLs natively. The vnc-plugin-vencrypt package adds TLS support; without it Remmina falls back to plaintext Plain auth.

#### SSH tunnel + any client (corporate-firewall workaround)

 ```
# Tunnel TCP 5901 through your SSH bastion to Scrapfly:
ssh -L 5901:browser.scrapfly.io:5901 you@bastion.example.com -N &

# Then connect any VNC client to the local end:
vncviewer 127.0.0.1::5901
# (password = {project_salt}-{vnc_password} as usual)
```

 Useful when your operators sit behind a corporate egress filter that blocks 5901 but allows SSH. The browser-based noVNC path (below) is a simpler alternative if outbound HTTPS is permitted.

## Browser-based clients (noVNC)

 If your operators sit behind a corporate firewall that blocks TCP 5901 or you want a zero-install attach experience, Scrapfly exposes the same VNC session as a WebSocket that noVNC and any RFB-over-WS client can drive directly from a browser tab.

WebSocket endpoint:

 ```
wss://browser.scrapfly.io/run/{run_id}/vnc
```

Two practical paths to use it:

1. **Drop-in HITL kit:** the [HITL kit](https://scrapfly.io/docs/cloud-browser-api/human-in-the-loop#embed-kit) bundles noVNC plus a WebRTC and CDP Screencast viewer in one vanilla-JavaScript file. Drop a `<canvas>` in your app, set the api key, the operator picks the transport. Recommended for self-hosted operator consoles.
2. **Raw noVNC:** point the upstream [noVNC](https://github.com/novnc/noVNC) client at the WebSocket URL above. Authenticate with the `vnc_password` you set at allocation. This is the same path the dashboard player uses internally.

## Receiving file downloads

 Native RFB does not carry file transfers, so files saved by the remote browser are exposed on a separate HTTP endpoint. Same agent filesystem, available to every transport. Authenticate with HTTP Basic and the session's `rtc_password` (or `vnc_password` if no RTC credentials were set; the username is your `rtc_username` or the literal `scrapfly`).

  `GET /run/{run_id}/downloads`: list available filesReturns a JSON array of every file currently sitting in the session's download dir on the agent.

**Response (200 OK, `application/json`):**

 ```
{
  "files": [
    {
      "filename": "invoice-2026-Q1.pdf",
      "size": 184523,
      "mtime_unix": 1748284619
    },
    {
      "filename": "export.csv",
      "size": 9821,
      "mtime_unix": 1748284701
    }
  ]
}
```

An empty list (`{"files": []}`) is returned when no downloads are pending. The list is not paginated, so a long-running session that downloads thousands of files should consume + delete on a loop rather than letting the response grow unbounded.

   `GET /run/{run_id}/downloads/{filename}`: stream a fileReturns the raw bytes with `Content-Disposition: attachment` so browsers offer a save dialog. The filename is the value from the listing response (URL-encode it if it contains spaces or special characters).

**Response codes:**

- `200 OK`: the file is streamed back.
- `400 Bad Request`: filename contains a path-traversal attempt (`/`, `\`, `.`, `..`).
- `404 Not Found`: no such file in the session's download dir.

   `DELETE /run/{run_id}/downloads/{filename}`: remove the server-side copyDrops the file from the agent's filesystem once the customer confirms the local copy is safe. Sessions are bounded in disk space; long runs should delete files after fetching them.

**Response:** `204 No Content` on success.

  Full usage with a polling one-liner that fetches and deletes new files into the current directory is in the [HITL doc](https://scrapfly.io/docs/cloud-browser-api/human-in-the-loop#standalone-vnc-downloads).

## Project salt response header

 Every successful Cloud Browser WebSocket upgrade carries an `X-Browser-Project-Salt` response header. The value is an opaque 8-character lowercase identifier scoped to your project. It is stable for a given api key, safe to log, and cannot be reversed to recover the api key.

What it is for:

- Confirm an attach link belongs to your project before sharing it with a HITL operator (compare the salt your SDK reports against the salt your operator can see at attach time).
- Tag audit-log entries with a stable per-project identifier without embedding the api key.
- Disambiguate multi-tenant logs.

 Every SDK exposes the same value without a network call. See the [project salt section](https://scrapfly.io/docs/cloud-browser-api/human-in-the-loop#project-salt) in the HITL doc for Python, TypeScript, Go, Rust, and CLI snippets.

 The header is set on the WebSocket upgrade response. Some browser-side WebSocket APIs do not expose response headers to JavaScript; the SDKs read it from the underlying transport, and the CLI helper computes it locally from the api key for cases where the header is not reachable.

 **HIPAA customers:** every VNC attach (TCP or WebSocket) is recorded in the session audit trail (`cloud_browser_agent_attach`) with the source IP, run id, and timestamp. When `debug=true` is set at allocation the session itself is recorded for replay. Both artifacts are retained for the audit-control evidence pack required by HIPAA §164.312(b).

## Billing

 Enabling VNC on a session adds a flat 5-credit fee on top of the normal Cloud Browser charges. There is no per-minute or bandwidth fee: connect, control, and disconnect freely during the session.

## Security

- **Use a strong password.** Pick 16 random characters or more. The same password is used by every client that attaches to this session.
- **Encrypted transport.** Modern clients (TigerVNC, RealVNC, Remmina with vencrypt plugin) negotiate VeNCrypt-X509Plain and tunnel the RFB stream inside TLS 1.2+. Apple Screen Sharing only speaks RFB 3.3 and falls back to plaintext on the wire — use TigerVNC if you need encryption end-to-end. The dashboard noVNC path is always TLS via the wss:// upgrade.
- **Password is session-scoped.** When the session ends, the password is gone. Future sessions can reuse the same password if you choose, but each session is authenticated independently.

## Troubleshooting

#### "Invalid endpoint: port not correctly specified" (RealVNC Viewer)

 RealVNC Viewer's address bar only accepts bare `host:port` — not the `vnc://` URI scheme. Remove the scheme prefix and any `{run_id}@` username portion: type just `browser.scrapfly.io:5901`. The run\_id goes in the username field of the auth dialog, not the address.

#### "Unencrypted connection" warning (RealVNC Viewer)

 Shown when the client picks plain VncAuth/Plain instead of VeNCrypt-X509Plain. Click **Continue** to proceed (auth-handshake password is still obfuscated by RFB DES challenge), or upgrade to a RealVNC build with full VeNCrypt-X509Plain support to get TLS on the framebuffer stream too.

#### "Authentication failed" / wrong password

 Most common cause: you typed the bare `vnc_password` instead of the salted form `{project_salt}-{vnc_password}`. The salt is the 8 hex chars from the `X-Browser-Project-Salt` response header at allocation time, deterministic per api key.

 Second cause: the password contains characters that need escaping in your shell. Wrap the value in single quotes when passing it via CLI flags or here-docs.

#### "RFB version unsupported" / "Protocol version mismatch"

 The mux speaks RFB 3.3 (Apple Screen Sharing default), RFB 3.7, and RFB 3.8. Anything else — typically very old VNC viewers or experimental builds — is rejected with a clear reason string. Upgrade to TigerVNC 1.10+ or RealVNC Viewer 6.0+ if you hit this.

#### TLS handshake errors with TigerVNC

 If TigerVNC reports "unknown CA" or "certificate verification failed", pass `-X509CA=` (empty) and `-X509CRL=` (empty) to skip CA verification. Scrapfly's mux presents a certificate signed by an internal CA that public trust stores don't recognize. The TLS encryption is still active — you're just trusting the cert on first use instead of by chain.

#### "Another human operator is already attached"

 Only ONE human VNC/RTC session may attach to a run at a time. Wait for the previous operator to disconnect, or stop the session and start a new one. The dashboard player and any direct VNC client compete for the same slot.

#### Black void / huge canvas in the viewer

 If you see a tiny rendered area at the top and a vast black region below, the agent's framebuffer has grown beyond the requested viewport. This was a known bug with older Cloud Browser agents and is fixed in current builds. If you still see it, the dashboard "Sessions" page will show your session config — file a support ticket with the run ID.

#### Session disconnects shortly after attach

 Apple Screen Sharing has a short read-timeout on the first ServerInit frame if the agent takes longer than 5s to send it. Retry the connect — the second attempt usually succeeds because the agent's x11vnc has cached more state. For repeatable failures, switch to TigerVNC which is more patient on slow connects.
