Skip to content

Migrating from Fortress (BSD releases)

If you run one of Fortress's BSD-licensed releases (v149, v150 or v151) as a CDP endpoint, you can move the setup to Clearcote by changing how the browser starts. The code that connects to the endpoint stays the same.

Fortress moved to its own source-available licence with v3 (Chromium 153) on 30 September 2026, and its earlier releases keep their BSD-3 licence. This guide is for teams on one of those BSD releases who would rather move to Clearcote than to v3. For licences, prices and platforms side by side, see Clearcote vs Fortress.

What changes

You start Fortress withStart Clearcote with
Python: Fortress() from tilion-fortress, then f.cdp_urlserve() from clearcote, then srv.cdp_url
Node: Fortress.launch(), then f.cdpUrlawait serve() from clearcote, then srv.cdpUrl
Docker: tilion/fortress:149, :150 or :151 on port 9222teamflatearth/clearcote on port 9222
The tilion launcher, or the binary with --remote-debugging-port=9222clearcote serve --port 9222

Everything after the connect call, your Playwright, Puppeteer, browser-use, Crawl4AI or Stagehand code, stays as it is. One thing to check first: the tilion/fortress:latest image tag has pointed at v3 since 30 September 2026, so a setup that pulls :latest is no longer on a BSD release. Pin :149, :150 or :151 until you have switched.

Python and Node: serve()

serve() starts Clearcote with the SDK's launch settings (persona, proxy, defaults) and a CDP endpoint on loopback, and returns a handle with the endpoint's URL. Pass port=9222 if other code expects the old address; without it, serve() picks a free port.

# pip install -U clearcote
from clearcote import serve
from playwright.sync_api import sync_playwright

with serve(fingerprint="acct-1", port=9222) as srv:        # was: with Fortress() as f:
    with sync_playwright() as p:
        browser = p.chromium.connect_over_cdp(srv.cdp_url)  # was: f.cdp_url
        page = browser.contexts[0].new_page()               # the served profile
        page.goto("https://example.com")
        print(page.title())
        browser.close()                                     # disconnect before the with blocks end

close() stops the browser, waits for it to exit and removes the temporary profile serve() made; in Python the with block calls it for you. For browser-use, Crawl4AI or Stagehand, give their CDP setting srv.cdp_url (Node: srv.cdpUrl). The .NET SDK has the same call as ServeAsync. More in Puppeteer and other CDP clients.

Docker

Swap the image and keep the port. The client connects to the same address as before.

bash
# was: docker run --rm -p 9222:9222 tilion/fortress:151
docker run -d --rm -p 127.0.0.1:9222:9222 -e CC_FINGERPRINT=acct-1 teamflatearth/clearcote

# the latest build: pass your key from the environment and keep the download in a volume
docker run -d --rm -p 127.0.0.1:9222:9222 -e CC_FINGERPRINT=acct-1 \
  -e CLEARCOTE_LICENSE_KEY -v clearcote-cache:/opt/xdg-cache teamflatearth/clearcote
python
browser = p.chromium.connect_over_cdp("http://localhost:9222")   # unchanged
page = browser.contexts[0].new_page()                            # the container's own profile

-p 127.0.0.1:9222:9222 keeps the endpoint on this machine: a CDP port is full control of the browser, so publish it only to networks you trust. Set CC_FINGERPRINT to choose or reuse an identity: without it, images from sdk-0.39.0 on give each container its own random seed, and older images give every container the same one. The image is configured entirely with environment variables (platform, language, timezone, proxy), listed in Deployment.

From the shell: clearcote serve

If you started the binary or the tilion launcher yourself and pointed clients at port 9222, clearcote serve (in the Python and Node packages) is the standing replacement. A connection with no parameters gets one shared default browser, so existing clients work unchanged; a connection can also ask for its own identity in the URL.

bash
# was: tilion --headless=new --remote-debugging-port=9222 --user-data-dir=/tmp/p
clearcote serve --port 9222

#   connect_over_cdp("http://127.0.0.1:9222")                     # the shared default browser
#   connect_over_cdp("http://127.0.0.1:9222?fingerprint=acct-1")  # a browser of its own for acct-1

What works differently

  • Identity comes from a seed. In the BSD releases, Fortress's launcher applies one coherent default Windows persona, and you change it surface by surface with --uxr-* switches (--uxr-timezone, --uxr-languages, --uxr-screen-width, --uxr-canvas-seed and so on) or TILION_TZ / TILION_LANG. Clearcote derives the whole persona, per-site canvas and WebGL noise included, from one fingerprint seed, so the same seed gives the same identity every time and a new seed gives an unrelated one. Give each account its own seed instead of porting the --uxr-* values one by one; set the platform with platform, and the timezone and languages with timezone and accept_language (Node: acceptLanguage), or let geoip match them to the proxy. See Recommended settings.
  • Open pages in the served profile. Use browser.contexts[0] as in the examples. With Puppeteer, connect with defaultViewport: null so the window keeps the size the persona gave it.
  • Give the proxy to Clearcote, not the client. Pass proxy to serve(), or --proxy to clearcote serve, and turn on geoip so the timezone, languages and WebRTC address match the exit. A proxy with a username and password needs the latest build; on the open build, use one that authorises by IP. See Proxies & geoip.
  • Fortress's own switches and environment variables do nothing here. Clearcote's options are in Launch options and Fingerprint flags.
  • Humanized input is a launch() option. It runs on the Playwright side, so it does not apply to a client attached over CDP.
  • Same platforms as the BSD releases: Windows x64 and Linux x64, and the Docker image is x64.

Which Clearcote build you get

Without a licence key, the SDK and the image run the open build: BSD-3, every patch public and reproducible, on Chromium 150, no account needed. With a key they run the latest build (Chromium 154), which adds private patches: free with GitHub for one browser at a time, and Pro for more. Save the key with clearcote login or set CLEARCOTE_LICENSE_KEY; see Installation.

Check the switch

With the endpoint running, ask it what it is:

bash
curl -s http://127.0.0.1:9222/json/version   # the browser behind the endpoint
clearcote info                                # SDK, licence, cached build and a launch test

clearcote info prints the build tag it resolves and Launch test ok with the exact browser version. The setup runbook lists which version each build should report, and what to do when a check fails.