{"slug": "kitesurf-now-open-source-cloudflare-s-agentic-browser-on-dioxus-blitz", "title": "Kitesurf now open source – Cloudflare's agentic browser on Dioxus Blitz", "summary": "Cloudflare has open-sourced Kitesurf, an experimental browser engine that runs on Cloudflare Workers and parses HTML, applies CSS, executes page JavaScript and renders screenshots without Chrome or a browser container. Kitesurf speaks the Chrome DevTools Protocol, so CDP clients such as Puppeteer can connect to it at ws://127.0.0.1:8787/devtools/browser, but running it on Cloudflare requires Dynamic Workers, which are part of the Workers Paid plan. The repository builds with Bazel and requires Node.js 22 or newer and pnpm 9.15.4, with local development started via pnpm run dev on port 8787.", "body_md": "Kitesurf is an experimental browser engine that runs on Cloudflare Workers. It parses HTML, applies CSS, runs page JavaScript, and renders screenshots without Chrome or a browser container.\n\nKitesurf speaks the [Chrome DevTools Protocol (CDP)](https://chromedevtools.github.io/devtools-protocol/),\nso you can use it with CDP clients such as Puppeteer.\n\nImportant\n\nKitesurf requires Cloudflare Dynamic Workers to create isolated workers for\npages. Dynamic Workers are part of the [Workers Paid plan](https://developers.cloudflare.com/dynamic-workers/pricing/).\nLocal development uses `wrangler dev`. A Cloudflare Workers Paid plan is\nrequired to run Kitesurf on Cloudflare.\n\n- [Bazelisk](https://github.com/bazelbuild/bazelisk) , installed as`bazel` .\nBazel builds everything. It downloads the pinned Bazel, Rust toolchains,\nwasm-bindgen, and binaryen itself.\n- Node.js 22 or newer\n- pnpm 9.15.4, which the repository pins in `package.json`\n- On macOS, GNU tar (`brew install gnu-tar` )\n- Rust through [rustup](https://rustup.rs/) , only for editing the vendored\ncrate patches.`rust-toolchain.toml` pins the same nightly that Bazel uses.\n\nInstall pnpm separately if it is not already available:\n\n```\nnpm install --global pnpm@9.15.4\n```\n\nClone the repository and install its dependencies:\n\n```\ngit clone https://github.com/cloudflare/kitesurf.git\ncd kitesurf\npnpm install --frozen-lockfile\n```\n\nBuild and start the local engine:\n\n```\npnpm run dev\n```\n\nThe engine listens on `http://127.0.0.1:8787`. Visit\n`http://127.0.0.1:8787/health` to confirm that it is running.\n\nThe root URL shows the landing page. It has a form to open a URL in Chrome\nDevTools, example pages, and the CDP endpoints for agents and other clients.\nSee [Use Chrome DevTools](#use-chrome-devtools).\n\n`pnpm run dev` builds the WebAssembly modules and the page and renderer bundles\nwith Bazel, then starts `wrangler dev`. The first build compiles the Rust core\nand its vendored crates, which takes a while; later builds reuse Bazel's cache.\n\nSet `KITESURF_PORT` when port `8787` is already in use:\n\n```\nKITESURF_PORT=8799 pnpm run dev\n```\n\nInstall `puppeteer-core` in the project that will control Kitesurf. Then connect\nto the browser WebSocket endpoint:\n\n``` python\nimport puppeteer from \"puppeteer-core\";\n\nconst browser = await puppeteer.connect({\n  browserWSEndpoint: \"ws://127.0.0.1:8787/devtools/browser\",\n});\n\nconst page = await browser.newPage();\nawait page.goto(\"https://example.com/\");\nawait page.screenshot({ path: \"page.png\" });\n\nawait browser.disconnect();\n```\n\nUse the port from `KITESURF_PORT` if you selected a different port. HTTP\ndiscovery is available at `http://127.0.0.1:8787/json/version`.\n\nWith `pnpm run dev`, open `http://127.0.0.1:8787/` in Chrome. Enter a URL to\ninspect, or select one of the example pages. DevTools opens in the tab on that\npage. To go to DevTools directly, open\n`http://127.0.0.1:8787/?url=https://example.com`, or\n`http://127.0.0.1:8787/?inspect=1` for a blank page.\n\nIf you built the bundled frontend, `pnpm run dev` serves it from the engine's\nown origin. See\n[Use the bundled DevTools frontend](#use-the-bundled-devtools-frontend).\nOtherwise, the DevTools frontend comes from\n`chrome-devtools-frontend.appspot.com`, the build that Google hosts for Chrome.\nThe hosted frontend has these limits:\n\n- The first time it connects, Chrome asks whether\n`chrome-devtools-frontend.appspot.com` can access apps on this device. Select**Allow** .\n- If you block this access, DevTools says that the WebSocket disconnected. To allow it again, click the site information icon to the left of the address. Then allow the access and reconnect DevTools.\n- The frontend can connect only to `127.0.0.1` . Thus it does not work with a\ndeployed engine.\n\nYou can also use `chrome://inspect`:\n\n1. Open `chrome://inspect/#devices` in Chrome. Type or paste it into the address\nbar because web pages cannot link to it.\n2. Select **Discover network targets** .\n3. Select **Configure...** and add`127.0.0.1:8787` .\n4. Under **Remote Target** , select**inspect** next to**kitesurf** .\n5. Enter a page URL in the address bar above the screencast.\n\nUse the port from `KITESURF_PORT` when you select a different local port. Chrome\ndiscovers targets with plain HTTP and WebSocket connections. The `chrome://inspect`\nworkflow cannot reach an HTTPS engine. Use it with a local engine instead.\n\nKitesurf provides a CDP endpoint, not a full Chrome implementation. Some Chrome\nDevTools features do not work yet. We are working to add more support over time.\nPuppeteer and other CDP clients can connect to\n`ws://127.0.0.1:8787/devtools/browser`.\n\nKitesurf can serve the Chrome DevTools frontend from its own origin. Then\nDevTools works without the hosted frontend, its permission prompt and the\n`127.0.0.1` limit. A deployed engine always uses this copy. The frontend is the\nprebuilt build that Google publishes for Chromium. The default build does not\ninclude it.\n\nThe `--config=devtools` Bazel option adds the frontend to the build. The first\nbuild with it downloads the frontend (about 250 MB). Bazel keeps the download\nin its cache for later builds.\n\nTo build the frontend once, run this command from the repository root:\n\n```\nbazel run --config=devtools //packages/engine:dev_build\n```\n\nTo include the frontend in every local build, add the option to\n`.bazelrc.user`. Git ignores this file. Then `pnpm run build` and\n`pnpm run dev` include the frontend.\n\n```\necho 'common --config=devtools' >> .bazelrc.user\n```\n\nThe build copies the frontend to `packages/engine/public/inspector/`. Git\nignores this directory. A build without the option removes it. To turn the\nfrontend off, remove the line from `.bazelrc.user`.\n\n1. Build the frontend as described above.\n2. Start the engine with `pnpm run dev` . The engine serves the bundled frontend\nwhen the build includes it, and the hosted frontend when it does not.\n3. Open `http://127.0.0.1:8787/` in Chrome. Enter a URL to inspect, or select\none of the example pages. DevTools opens in the tab on that page.\n\nTo start on a page, open `http://127.0.0.1:8787/?url=https://example.com`.\n\nUse the smallest step that fixes the problem:\n\n- \nTo copy the frontend files again, delete the copy and build again: \n\n```\nrm -rf packages/engine/public/inspector\nbazel run --config=devtools //packages/engine:dev_build\n```\n\n- \nTo extract the archive again, for example after you change `bazel/devtools_frontend.BUILD` , fetch it again and then build:\n\n```\nbazel fetch --force --repo=@devtools_frontend --config=devtools\nbazel run --config=devtools //packages/engine:dev_build\n```\n\n Bazel extracts the archive from its cache. To download the archive again from Google, add `--repository_cache=` to the`bazel fetch` command.\n\nDo not use `bazel clean --expunge` for this. It also removes the WebAssembly\nbuild and the rest of the Bazel cache.\n\nTwo pins select the frontend version:\n\n- `DEVTOOLS_FRONTEND_REV` and the values after it in`MODULE.bazel` select the\nbundled frontend. The comment above them tells you how to select a new\nsnapshot.\n- `KITESURF_INSPECTOR_FALLBACK_URL` in the`local` environment of`packages/engine/wrangler.jsonc` selects the hosted frontend. Use the\nChrome commit that`https://chromiumdash.appspot.com/fetch_version?version=<chrome-version>` returns in`hashes.chromium` .\n\nKeep the two pins on the same Chrome version. After you change\n`MODULE.bazel`, the next build downloads the new archive.\n\nThe pnpm scripts are front ends to Bazel:\n\n```\npnpm run build                    # bazel run //packages/engine:dev_build\npnpm run check                    # type checks, bundles, and repository checks\npnpm run test:unit                # engine vitest and Rust host tests\npnpm run test:e2e <file-substring>\npnpm run format:check             # oxfmt; pnpm run format rewrites the same files\npnpm run format:rust              # format Rust with Bazel's pinned rustfmt\npnpm run lint                     # oxlint, the custom lint rules, and Rust lint\n```\n\nThe Rust formatter rewrites tracked and non-ignored untracked files, excluding\npaths in `.oxfmtrc.json`.\n\n`pnpm run build` copies the Bazel-built WebAssembly, bundles, and RBI files into\nthe source tree, where `wrangler dev` reads them. Other targets run directly,\nfor example `bazel test //tests/e2e:gate` for the full e2e suite. Another Bazel\nworkspace can depend on this repository as a module.\n\nKitesurf combines several open-source Rust projects:\n\n- [Blitz](https://github.com/DioxusLabs/blitz) provides HTML parsing, the DOM,\nlayout, and painting. It is licensed under`MIT OR Apache-2.0` .\n- [Stylo](https://github.com/servo/stylo) provides CSS parsing, cascading, and\ncomputed styles. It is licensed under`MPL-2.0` .\n- [Boa](https://github.com/boa-dev/boa) runs page JavaScript inside WebAssembly.\nIt is licensed under`Unlicense OR MIT` .\n- [Linebender](https://linebender.org/) libraries provide text layout and 2D\ngraphics, including[Parley](https://github.com/linebender/parley) ,[Vello](https://github.com/linebender/vello) ,[Kurbo](https://github.com/linebender/kurbo) , and[Peniko](https://github.com/linebender/peniko) . These libraries are licensed\nunder`Apache-2.0 OR MIT` .\n- [Krilla](https://github.com/LaurenzV/krilla) generates PDF output. It is\nlicensed under`MIT OR Apache-2.0` .\n\nKitesurf patches its Rust dependencies. The patch series are in\n[`scripts/patches/`](https://github.com/cloudflare/kitesurf/blob/main/scripts/patches), and Bazel applies them itself.\n\nWarning\n\nDo not deploy Kitesurf without authentication in front of it. Use\n[Cloudflare Access](https://developers.cloudflare.com/workers/configuration/routing/workers-dev/#manage-access-to-workersdev)\n(Cloudflare Zero Trust) or another authentication layer. The engine has no\nauthentication of its own. A person who has the URL can:\n\n- Open DevTools and browse the web through your engine.\n- Connect Puppeteer and other CDP clients to it.\n- Send outbound requests that come from your Cloudflare account.\n- Use CPU time and Dynamic Workers that your account pays for.\n\nThe `workers.dev` URL is public by default. Its name follows a predictable\npattern: `kitesurf.<your-subdomain>.workers.dev`.\n\nTo deploy the engine as a Worker on your Cloudflare account:\n\n1. \nDeploy the engine. Run the command from the repository root. In a package directory, use `pnpm -w run deploy` .\n\n```\npnpm run deploy\n```\n\n2. \nProtect the Worker URL with Cloudflare Access immediately. Follow [Manage access to `workers.dev`](https://developers.cloudflare.com/workers/configuration/routing/workers-dev/#manage-access-to-workersdev) .\nFor a custom domain or route, protect it with a[self-hosted Access application](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-public-app/) .\n3. \nOpen the Worker URL in Chrome. Access asks you to sign in. Then the landing page opens. Enter a URL to open it in DevTools.\n\nBetween steps 1 and 2, the engine is public. To prevent this gap, turn on Access\nfor all Workers in the account before you deploy. The `workers.dev` page in\nstep 2 tells you how.\n\nThe command builds the engine with Bazel and uploads it with Wrangler. Wrangler\nuses the account of `wrangler login`, or `CLOUDFLARE_ACCOUNT_ID` and\n`CLOUDFLARE_API_TOKEN`. The command sends other arguments to `wrangler deploy`,\nfor example `--name <worker-name>`. Without `--name`, the Worker name is\n`kitesurf`.\n\nTo build the deploy without an upload, run `pnpm run deploy --dry-run`.\n\nTo upload a version without sending traffic to it, run `pnpm run deploy:preview`.\nThe command runs `wrangler versions upload`. If the Worker has Version URLs\nturned on, Wrangler prints the URL of the new version. Version URLs are public\ntoo. Protect them with Access as well. Follow\n[Manage access to Version URLs](https://developers.cloudflare.com/workers/versions-and-deployments/version-urls/#manage-access-to-version-urls).\n\nThe deploy always builds with `--config=devtools`, so it includes the Chrome\nDevTools frontend. You do not need `.bazelrc.user` for it. The first deploy\ndownloads the frontend, as the first local build does. The deploy sets\n`KITESURF_INSPECTOR_BASE_URL` to `/inspector/inspector.html`. To use a different\nfrontend, add `--var KITESURF_INSPECTOR_BASE_URL:<url>` to the command.\n\nDevTools loads from the Worker's own origin, so its WebSocket sends the Access cookie. A DevTools frontend on another origin cannot connect through Access.\n\nAccess blocks a CDP client that has no browser session. To connect Puppeteer:\n\n1. \nCreate an [Access service token](https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/) .\n2. \nAdd a policy with the **Service Auth** action that allows this token.\n3. \nSend the token headers with the WebSocket connection: \n\n``` js\nconst browser = await puppeteer.connect({\n  browserWSEndpoint: \"wss://<worker-host>/devtools/browser\",\n  headers: {\n    \"CF-Access-Client-Id\": process.env.CF_ACCESS_CLIENT_ID,\n    \"CF-Access-Client-Secret\": process.env.CF_ACCESS_CLIENT_SECRET,\n  },\n});\n```\n\nKeep the service token secret. The token gives the same access as a signed-in user.\n\nThe engine configuration includes a Worker Loader binding. Kitesurf uses this\nbinding to start a separate worker for each page and renderer. Cloudflare\nDynamic Workers provide this capability, and they require the Workers Paid plan.\nSee the [Dynamic Workers pricing](https://developers.cloudflare.com/dynamic-workers/pricing/)\ndocumentation before planning a Cloudflare deployment.\n\n| Package | Description | \n|---|---|\n| [`engine`](https://github.com/cloudflare/kitesurf/blob/main/packages/engine) | Cloudflare Worker and CDP server | \n| [`page-script`](https://github.com/cloudflare/kitesurf/blob/main/packages/page-script) | Page DOM and JavaScript runtime | \n| [`page-renderer`](https://github.com/cloudflare/kitesurf/blob/main/packages/page-renderer) | Renderer for recorded page scenes | \n| [`dom-shim`](https://github.com/cloudflare/kitesurf/blob/main/packages/dom-shim) | DOM APIs inside the page worker | \n| [`core`](https://github.com/cloudflare/kitesurf/blob/main/packages/core) | Rust layout, paint, and JavaScript engine code | \n| [`renderer-core`](https://github.com/cloudflare/kitesurf/blob/main/packages/renderer-core) | Rust scene rasterizer for page rendering | \n| [`scene-host-wasm`](https://github.com/cloudflare/kitesurf/blob/main/packages/scene-host-wasm) | Local scene rasterizer for the terminal client | \n| [`kitesurf-cli`](https://github.com/cloudflare/kitesurf/blob/main/packages/kitesurf-cli) | Terminal client | \n\nRead [CONTRIBUTING.md](https://github.com/cloudflare/kitesurf/blob/main/CONTRIBUTING.md) before opening an issue or pull request.\nReport security problems as described in [SECURITY.md](https://github.com/cloudflare/kitesurf/blob/main/SECURITY.md).\n\nKitesurf is licensed under [Apache 2.0](https://github.com/cloudflare/kitesurf/blob/main/LICENSE). Bundled fonts, data, and\ndependencies keep their own licenses. See [NOTICE](https://github.com/cloudflare/kitesurf/blob/main/NOTICE).", "url": "https://wpnews.pro/news/kitesurf-now-open-source-cloudflare-s-agentic-browser-on-dioxus-blitz", "canonical_source": "https://github.com/cloudflare/kitesurf", "published_at": "2026-11-13 21:10:05+00:00", "updated_at": "2026-11-13 21:20:19.542311+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Cloudflare", "Kitesurf", "Dioxus Blitz", "Chrome DevTools Protocol", "Puppeteer", "Cloudflare Workers", "Dynamic Workers", "Bazel"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/kitesurf-now-open-source-cloudflare-s-agentic-browser-on-dioxus-blitz", "markdown": "https://wpnews.pro/news/kitesurf-now-open-source-cloudflare-s-agentic-browser-on-dioxus-blitz.md", "text": "https://wpnews.pro/news/kitesurf-now-open-source-cloudflare-s-agentic-browser-on-dioxus-blitz.txt", "jsonld": "https://wpnews.pro/news/kitesurf-now-open-source-cloudflare-s-agentic-browser-on-dioxus-blitz.jsonld"}}