Skip to content
txchenPublic

About

cloakbrowser in docker

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

72 Commits

Folders and files

Repository files navigation

CloakHub

Run persistent browsers on your own server and let agents use them from another machine. CloakHub manages CloakBrowser profiles with saved logins, a live browser viewer, and remote automation. Idle browsers stop to free resources and wake when you connect again; their profile data stays on disk.

  • Persistent identities: each profile keeps cookies, storage, fingerprint settings, and proxy configuration.
  • On-demand browsers: automatic wake-up, configurable idle sleep, and a running-instance limit.
  • Human and agent access: use the web viewer for login or manual work and the companion skill for automation.
  • Simple deployment: Docker Compose on Linux, with amd64 and arm64 images.

How it works

flowchart LR
  subgraph client[Client machine]
    agent[AI agent]
    skill[CloakHub browser skill]
    config[client.json and CDP token]
    agent --> skill
    config --> skill
  end
  human[Your web browser]
  subgraph server[Linux server - Docker Compose]
    hub[CloakHub - port 7788]
    browser[CloakBrowser instances]
    display[KasmVNC - live viewer]
    data[(Persistent data volume)]
    hub -->|CDP and lifecycle| browser
    hub -->|Viewer proxy| display
    display --- browser
    hub -->|Profile metadata| data
    browser -->|Cookies and browser storage| data
  end
  skill -->|CDP over WebSocket| hub
  human -->|Web UI and live viewer| hub
  browser --> sites[Websites]
Loading

The server runs the browsers. The client runs the agent and a small Node/Playwright script supplied by the skill; it needs no local browser, browser license, or MCP server. A client target alias selects the hub URL, exact profile ID, and profile CDP token together.

Docker

You need a Linux host with Docker Compose and a CloakBrowser key that permits the selected browser build. No source checkout or local image build is required. If you cannot provide a license key, skip ahead to Run the free image. Create a deployment directory and save this as compose.yml (also available here):

services:
  cloakhub:
    image: ghcr.io/txchen/cloakhub:0.9.0
    restart: unless-stopped
    shm_size: 2gb
    environment:
      CLOAKHUB_LICENSE_KEYS_FILE: /run/secrets/cloakbrowser-keys
      CLOAKHUB_DATA_DIR: /data
      CLOAKHUB_HOST: 0.0.0.0
      CLOAKHUB_PORT: "7788"
      CLOAKHUB_DISK_CACHE_SIZE_MB: "${CLOAKHUB_DISK_CACHE_SIZE_MB:-256}"
      CLOAKHUB_DEFAULT_TIMEZONE: "${CLOAKHUB_DEFAULT_TIMEZONE:-UTC}"
      CLOAKHUB_DEFAULT_LOCALE: "${CLOAKHUB_DEFAULT_LOCALE:-en-US}"
      CLOAKHUB_AUTH_TOKEN: "${CLOAKHUB_AUTH_TOKEN:?Set CLOAKHUB_AUTH_TOKEN in .env}"
    ports:
      - "${CLOAKHUB_BIND_ADDRESS:-127.0.0.1}:7788:7788"
    volumes:
      - ./data:/data
      - ./secrets/cloakbrowser-keys:/run/secrets/cloakbrowser-keys:ro

Create .env beside it, replacing the admin password with a value generated by openssl rand -hex 32. The following example allows access from your private LAN:

CLOAKHUB_AUTH_TOKEN=replace-with-a-random-admin-password
CLOAKHUB_BIND_ADDRESS=0.0.0.0
CLOAKHUB_DEFAULT_TIMEZONE=America/Los_Angeles
CLOAKHUB_DEFAULT_LOCALE=en-US

Choose the timezone and locale for your browser's internet exit. Omit CLOAKHUB_BIND_ADDRESS to bind only to server localhost, for example behind a local reverse proxy. For internet access, use HTTPS and a proxy supporting WebSocket upgrades. Set X-Forwarded-Host and X-Forwarded-Proto to the public origin and overwrite client-supplied forwarded headers. Mount CloakHub at the origin root, not a path prefix.

Prepare storage, then create secrets/cloakbrowser-keys with one CloakBrowser key per line:

mkdir -p secrets data
chmod 700 secrets
# Save your key(s) in secrets/cloakbrowser-keys, then:
chmod 600 .env secrets/cloakbrowser-keys
docker compose pull
docker compose up -d
docker compose logs -f cloakhub

Open http://<server-lan-ip>:7788 (or your HTTPS origin) and sign in with the admin password. With localhost binding, open http://localhost:7788 on the server or use your reverse proxy. The image automatically selects amd64 or arm64.

Version 0.9.0 bundles the latest public CloakBrowser binary: 146.0.7680.177.5 on amd64 and 146.0.7680.177.4 on arm64, verified by SHA-256 at build time. No browser download occurs at startup. The image includes Mac fonts and defaults new profiles to a macOS identity. No license key is bundled; the standard Hub still requires one. Keep ./data across container replacements: it contains profile metadata, browser storage, and secrets. See browser builds and the preview/stable comparison for details.

The two published images

The same release publishes two images:

Image License key Browser Notes
ghcr.io/txchen/cloakhub Required Public 146 build, baked in and SHA-256 verified amd64 and arm64
ghcr.io/txchen/cloakhub_free None Patched 154 build, baked in and SHA-256 verified amd64 only

Both default to a macOS persona, bundle Mac fonts, and otherwise share the same Hub features and settings.

Run the free image (cloakhub_free)

Use the free image when you cannot or do not want to supply a CloakBrowser key. It requires no secret file, does not contact the license service, and does not download a browser at runtime. It is amd64 only; on arm64, use the standard image instead.

The free image sets CLOAKHUB_LICENSE_MODE=none and already defaults CLOAKHUB_DATA_DIR, CLOAKHUB_HOST, CLOAKHUB_PORT, and CLOAKHUB_MAX_RUNNING_INSTANCES=10, so the Compose file only needs the settings an operator typically customizes. The Hub reads no license key, never queries license capacity, and never downloads a browser. It uses the license-free CloakBrowser build from the 154.0.8037.57.1 patched release, verified by SHA-256 at image-build time. Concurrent Browser Instances are capped only by CLOAKHUB_MAX_RUNNING_INSTANCES (default 10; add it to the Compose environment to raise it).

Save compose.free.yml as compose.yml in your deployment directory:

services:
  cloakhub-free:
    image: ghcr.io/txchen/cloakhub_free:0.9.0
    restart: unless-stopped
    shm_size: 2gb
    environment:
      CLOAKHUB_DISK_CACHE_SIZE_MB: "${CLOAKHUB_DISK_CACHE_SIZE_MB:-256}"
      CLOAKHUB_DEFAULT_TIMEZONE: "${CLOAKHUB_DEFAULT_TIMEZONE:-UTC}"
      CLOAKHUB_DEFAULT_LOCALE: "${CLOAKHUB_DEFAULT_LOCALE:-en-US}"
      CLOAKHUB_AUTH_TOKEN: "${CLOAKHUB_AUTH_TOKEN:?Set CLOAKHUB_AUTH_TOKEN in .env}"
    ports:
      - "${CLOAKHUB_BIND_ADDRESS:-127.0.0.1}:7788:7788"
    volumes:
      - ./data:/data

Create .env beside it with an admin password. No secrets/cloakbrowser-keys file or secrets directory is needed:

CLOAKHUB_AUTH_TOKEN=replace-with-a-random-admin-password
CLOAKHUB_BIND_ADDRESS=0.0.0.0
CLOAKHUB_DEFAULT_TIMEZONE=America/Los_Angeles
CLOAKHUB_DEFAULT_LOCALE=en-US

Then start it:

mkdir -p data
chmod 600 .env
docker compose pull
docker compose up -d
docker compose logs -f cloakhub-free

Open http://<server-lan-ip>:7788 (or your HTTPS origin) and sign in with the admin password. Keep ./data across container replacements. To pin a different release, change the image: tag; on a release, latest also points at the free image. The rest of this guide (profiles, client setup, and CDP) applies unchanged.

Prepare a browser profile

  1. Create a profile in the web UI with an exact ID such as research.
  2. Open its viewer and sign into any websites the agent should use. Default headed mode supports unattended automation; you do not need to leave the viewer open.
  3. To protect CDP access, open the profile's ··· menu and select Manage CDP token. In the CDP token section, click Protect with token, then Copy CDP URL to retrieve the generated token. It is not an editable field in Edit settings; the UI does not accept a custom token.
  4. Give the client the hub origin, profile ID, and CDP token if configured.

There are three separate credentials:

Credential Where it belongs Purpose
CloakBrowser key Server secret file Download and run the browser
CloakHub admin password Operator / management API Manage profiles and the server UI
Profile CDP token Client config, secret file, or environment Operate one profile's browser

If CDP access is protected, the agent needs only the profile CDP token. An admin password does not replace it. A profile without a CDP token has unprotected CDP endpoints, even when admin authentication is enabled.

Optional browser launch tips

In a profile's Edit settings page, add arguments under Custom launch arguments, one per line. They apply the next time the browser starts, so restart the profile after changing them. These switches can affect site behavior or the browser fingerprint; test them on a separate profile if compatibility matters.

Argument What it does Notes
--blink-settings=imagesEnabled=false Prevents image loading and can reduce page bandwidth. Image-dependent layouts, maps, CAPTCHA challenges, and buttons may not work correctly.
--disable-background-networking Reduces some browser background network activity. Does not block requests made by websites; may affect browser background services.
--disable-notifications Suppresses website notification prompts. Convenience only; does not reduce page traffic.
--mute-audio Mutes browser audio output. Does not stop audio or video from downloading.

For automation, Playwright can block image requests for a task. Add the route before navigating; it applies to pages in that profile context while the client is connected:

await context.route("**/*", route =>
  route.request().resourceType() === "image"
    ? route.abort()
    : route.continue()
);

This is per-client automation behavior, unlike a launch argument, and may change how browser caching works while routing is enabled.

Install the agent skill

On the client machine, install Node.js 20+ and run:

npx skills add txchen/cloakhub --skill cloakhub-browser --global

Select your agent clients and the default symlink installation mode when prompted. The global skill directory is normally ~/.agents/skills/cloakhub-browser in that mode. Install its pinned Node dependency there:

npm ci --prefix "$HOME/.agents/skills/cloakhub-browser"

If you choose copy mode or a different installation location, use the directory printed by the installer instead. Reload your agent's skills or restart its session. The skills CLI installs the skill files; npm ci installs playwright-core without downloading a local browser. After a skill update, run npm ci in the installed directory again.

Configure the client

Create the client configuration directory:

mkdir -p "$HOME/.config/cloakhub"
chmod 700 "$HOME/.config/cloakhub"

Save this as ~/.config/cloakhub/client.json, replacing the example origin and profile ID with yours:

{
  "version": 1,
  "defaultTarget": "work",
  "targets": {
    "work": {
      "url": "http://192.168.1.50:7788",
      "profile": "research",
      "token": "REPLACE_WITH_PROFILE_CDP_TOKEN"
    }
  }
}

Restrict access to the config file, which now contains the CDP token:

chmod 600 "$HOME/.config/cloakhub/client.json"

work is a client-local alias; research is the exact server profile ID, not its display name. url is the hub origin without /api/.... The helper reads the token directly; keep client.json out of prompts and committed files.

For multiple profiles or servers, add entries under targets, each with its own url, profile, and credential. The same skill works for all of them:

  • No explicit selection: use defaultTarget.
  • A request such as “use the personal browser”: the agent passes --target personal.
  • CLOAKHUB_TARGET=work in the agent's execution environment overrides the default; explicit --target takes precedence over both.
  • CLOAKHUB_CLIENT_CONFIG selects a different config file. No project-local config is automatically loaded.

The older "tokenFile": "tokens/work" (relative to client.json) and "tokenEnv": "WORK_CLOAKHUB_CDP_TOKEN" options are still supported if you prefer them. Use only one of token, tokenFile, or tokenEnv per target. If the profile has no CDP token, omit all three from its target; "auth": "none" is optional. For example:

"work": { "url": "http://192.168.1.50:7788", "profile": "research" }

This allows anyone who can reach the CDP endpoint to connect, even if the admin UI requires a password. If a token source is configured but its credential is missing, the helper fails rather than falling back to anonymous access. It does not choose an arbitrary profile or fall back to a local browser.

Verify the setup:

node "$HOME/.agents/skills/cloakhub-browser/scripts/browser.mjs" targets
node "$HOME/.agents/skills/cloakhub-browser/scripts/browser.mjs" check --target work

targets lists local connection metadata without reading tokens or contacting the server. check makes an authenticated CDP connection, wakes the profile if needed, reports "ok": true, and disconnects. Then ask your agent, for example:

Use the CloakHub work browser to open example.com and tell me the page title.

See the skill for the browser workflow and client setup reference for troubleshooting.

Use any CDP client

CloakHub exposes standard Chrome DevTools Protocol (CDP) endpoints. Existing CDP-based tools such as Playwright and Puppeteer can connect directly: point their remote-browser connection at your profile's endpoint and, if configured, supply its CDP token. No CloakHub SDK, agent skill, or admin credential is required for browser operations.

Install Playwright and CloakBrowser's client package on your client machine to use its Humanize behavior layer with the remote browser:

npm install playwright-core cloakbrowser

humanizeBrowser() patches the connected browser's existing and new pages. It adds human-like behavior to supported mouse, keyboard, and scrolling actions; it does not turn navigation, DOM reads, or arbitrary page.evaluate() calls into human actions.

Save this as browse.mjs. Replace the example origin and profile ID. If the profile has a CDP token, provide it through CLOAKHUB_CDP_TOKEN; otherwise leave that variable unset. Omitting the client token does not bypass a token configured on the server. This example opens Device & Browser Info's bot test, prints its detection results, and saves a screenshot on the client:

import { chromium } from 'playwright-core';
import { humanizeBrowser } from 'cloakbrowser';

const token = process.env.CLOAKHUB_CDP_TOKEN;

const browser = await chromium.connectOverCDP(
  'https://browser.example.com/api/profiles/research/cdp',
  {
    headers: token ? { Authorization: `Bearer ${token}` } : {},
    timeout: 60_000 // Allow time to wake a stopped browser.
  }
);

try {
  // Recommended for interactive automation: use CloakBrowser's Humanize behavior layer.
  await humanizeBrowser(browser, { humanize: true, humanPreset: 'default' });

  const context = browser.contexts()[0]; // Reuse the profile's saved login state.
  const page = await context.newPage();
  try {
    await page.goto('https://deviceandbrowserinfo.com/are_you_a_bot', {
      waitUntil: 'domcontentloaded', timeout: 60_000
    });
    // Wait for the site's asynchronous report, not just the initial page load.
    const report = page.locator('#jsonResult').filter({ hasText: /"isBot"\s*:/ });
    await report.waitFor({ state: 'visible', timeout: 60_000 });
    console.log(JSON.stringify(JSON.parse(await report.innerText()), null, 2));
    await page.screenshot({ path: 'bot-detection.png', fullPage: true });
  } finally {
    await page.close(); // Close only the tab this script created.
  }
} finally {
  await browser.close(); // Playwright disconnects this remote CDP client.
}

Run node browse.mjs (with the token in its environment if required). The connection automatically starts a stopped profile; no separate launch request is needed. Clients accepting a WebSocket endpoint can use wss://browser.example.com/api/profiles/research/cdp with the same authorization header. For a private HTTP deployment, use http:// / ws:// instead.

Inspect the site's isBot verdict and signals such as isAutomatedWithCDP, isAutomatedWithCDPInWebWorker, isPlaywright, and hasWebdriverTrue. A true detection signal means that particular check flagged the browser. This test covers fingerprinting signals, not IP reputation or user behavior; passing it is not a guarantee against detection on other sites. Results depend on the browser build, profile settings, automation client, and the site's current checks. If the report times out, treat the test as incomplete, not as a pass; the site's DOM may also change.

Disconnect when finished so idle shutdown can reclaim resources. Reconnect through the same fixed profile URL after a stop or restart; existing connections and in-flight commands are not automatically resumed. Compatibility follows each library's CDP support; CloakHub does not implement Playwright's separate browser-server protocol. See the API guide for discovery and authentication details.

Everyday use

Select a profile in the sidebar to open its viewer or controls. Use its menu to edit settings, manage its token, or start/stop it. Close viewer disconnects the viewer only. The event log shows starts, failures, idle stops, and capacity events.

Browsers wake automatically through their fixed profile connection URL. The skill disconnects after each script so idle sleep can work. An open CDP connection prevents automatic sleep; simply watching the viewer does not. Sleep retains cookies and browser storage but closes all tabs; the next start opens a new tab. Live JavaScript and unfinished operations are lost.

Multiple clients using the same profile share tabs and login state; aliases do not provide task isolation. Coordinate access or assign separate profiles for parallel work. Explicit Stop/Restart disconnects every client. Delete removes the profile's stored data. Launch-setting edits apply on the next start; deployment defaults affect new profiles only. CloakHub itself does not apply automatic GeoIP or SDK Humanize. The example above opts into Humanize client-side; set timezone and locale explicitly for the browser profile.

Server settings and upgrades

Common settings can be added under environment in Compose. Only variables explicitly referenced by compose.yml are picked up from .env.

Setting Default / purpose
CLOAKHUB_MAX_RUNNING_INSTANCES 10; browser key quotas may lower the effective limit
CLOAKHUB_DEFAULT_PLATFORM macos in Docker; linux in source runs without an override
CLOAKHUB_DEFAULT_TIMEZONE / CLOAKHUB_DEFAULT_LOCALE Compose uses UTC / en-US; set these to your region
CLOAKHUB_DISK_CACHE_SIZE_MB 256 per profile; HTTP cache budget, not a profile disk quota
CLOAKHUB_BROWSER_VERSION / CLOAKHUB_BROWSER_CHANNEL Exact build and channel for the optional runtime installer; unused with a bundled binary
CLOAKHUB_BROWSER_BIN Docker defaults to /opt/cloakbrowser/chrome; override with a mounted binary if needed
CLOAKHUB_LICENSE_MODE required; set none to skip license loading and capacity checks (the free image sets none)
CLOAKHUB_LICENSE_KEYS_FILE Browser keys; see multiple-key configuration

Pin a full image tag such as 0.9.0 for predictable upgrades. The 0.9 alias follows patch releases; latest follows releases, and master / sha-* are development tags. Browser builds stay pinned until you change the selected build or image. The free image, ghcr.io/txchen/cloakhub_free, follows the same tags.

Before upgrading, stop the service with docker compose stop and back up the entire ./data directory while browsers are stopped. Update the image tag, then run docker compose pull and docker compose up -d. Retain the old image tag and backup for rollback; do not assume older browsers can read profiles upgraded by newer builds.

Further documentation

About

cloakbrowser in docker

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages