Skip to main content

deva-cloak

Drive CloakBrowser stealth Chromium inside the deva cloak container - a headed, anti-detection browser for scraping, automation, or checking a site the way a real browser sees it. Use when the task needs a browser that bypasses bot detection (Cloudflare, FingerprintJS, reCAPTCHA scoring), or when the user asks to browse/scrape a protected site from inside deva. Only applies in the cloak image (verify with `command -v cloakbrowser`; DEVA_CLOAK=1 is the marker); the base/rust images have plain Playwright instead.

Source facts

Repository
thevibeworks/deva
Last source activity
July 27, 2026 at 09:32
Detected SKILL.md language
English
Stars
13
Forks
4

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
deva-cloak
description
Drive CloakBrowser stealth Chromium inside the deva cloak container - a headed, anti-detection browser for scraping, automation, or checking a site the way a real browser sees it. Use when the task needs a browser that bypasses bot detection (Cloudflare, FingerprintJS, reCAPTCHA scoring), or when the user asks to browse/scrape a protected site from inside deva. Only applies in the cloak image (verify with `command -v cloakbrowser`; DEVA_CLOAK=1 is the marker); the base/rust images have plain Playwright instead.
# deva-cloak: stealth browser in the deva cloak container CloakBrowser is a custom-compiled Chromium with source-level fingerprint patches. Same Playwright API, different binary -- detection sites score it as a real browser because it is one. This image runs it **headed by default** on a virtual display, which passes checks that headless fails even with the patches. ## Am I in the cloak context? This skill only applies when the browser is actually present. ALWAYS run this check fresh -- never conclude from conversation context, a resumed session, or `$DEVA_CLOAK` alone (env markers can be scrubbed or stale; the binary is the ground truth): ```bash command -v cloakbrowser >/dev/null && cloakbrowser info --quick ``` If that fails, you are in the base or rust image -- there is no CloakBrowser here. (`DEVA_CLOAK=1` is baked into the cloak image as a convenience marker, but treat it as a hint, not the test.) Tell the user to relaunch with `deva.sh -p cloak <agent>`, or use the plain Playwright install (rust image) instead. Do not try to `npm install cloakbrowser` yourself; the binary is baked into the cloak image and a runtime install would download ~200MB and defeat the point. ## What is baked in - Wrapper + `playwright-core` live in `$CLOAK_APP_DIR` (`/opt/cloak`). - Chromium binary is pre-downloaded, cache at `/opt/cloakbrowser`, auto-update off -- fully offline, no network needed to launch. - Free binary = **Chromium 146** (58 fingerprint patches). No Pro license. Good enough for most sites and for viewing your own apps; the newest detection tricks (latest Cloudflare/DataDome) may still win. Pro (146->150, 71 patches) needs `CLOAKBROWSER_LICENSE_KEY` at runtime -- no rebuild. - Headed display: Xvfb on `:99` + openbox, started by the entrypoint. - `x11vnc` for VNC into that display (interactive logins). Start it on demand (`x11vnc -display :99 -rfbport 5900 -bg`); on OrbStack/Linux the Mac reaches it directly at the container IP -- no port publish, no launch flag. - `cloak-browserd` always-on browser daemon (`/usr/local/lib/cloak/cloak-browserd.mjs`); OPT-IN, runs only with `--cloak-browser`, exposes CDP at `CLOAK_CDP_ENDPOINT`. Independent of VNC. ## Running a script CloakBrowser is ESM-only, so run the script **from `$CLOAK_APP_DIR`** where its `node_modules` resolves. Write output to absolute workspace paths. ```bash cd "$CLOAK_APP_DIR" node --input-type=module <<'EOF' import { launch } from 'cloakbrowser'; // Headed is the whole point of this image -- keep headless:false for // detection-heavy sites. Flip to true only for speed on trusted targets. const browser = await launch({ headless: false }); const page = await browser.newPage(); await page.goto('https://example.com', { waitUntil: 'domcontentloaded' }); console.log(await page.title()); await browser.close(); EOF ``` The returned object is a normal Playwright `Browser` -- `newPage`, `newContext`, `screenshot`, etc. all work unchanged. ## Options that matter Pass to `launch({ ... })`: - `headless: false` -- default posture here; some sites detect headless even with the C++ patches. - `humanize: true` -- human-like mouse curves, per-char typing, scroll. Turn on for behavioral detection (deviceandbrowserinfo, reCAPTCHA v3 scoring). - `proxy: 'http://user:pass@host:port'` or `'socks5://...'` -- residential IPs pass reputation checks that datacenter IPs fail. Add `geoip: true` to match timezone/locale to the proxy exit IP. - `args: ['--fingerprint=12345']` -- fixed seed = a consistent returning-visitor identity across runs (better for reCAPTCHA v3 than a fresh identity each time). Recommended stack for a hard anti-bot target: `launch({ headless: false, humanize: true, proxy: '<residential>', geoip: true })`. ## Authenticated sites: log in over VNC You cannot copy the host browser's cookie DB in -- Chrome encrypts cookie values with an OS-keychain key the Linux container can't reach, so the blobs are undecryptable here. Instead the human logs in once over VNC, watching a browser YOU (the agent) drive on the cloak display -- same browser, same profile, persisted to the host. **The human launches normally -- no VNC flag needed on OrbStack/Linux:** ```bash deva.sh -p cloak claude ``` **You (the agent) start VNC on demand when a login is needed.** OrbStack (and native Linux Docker) route the container straight from the host, so a server you start INSIDE the container is reachable from the Mac with no port publish and no launch flag: ```bash x11vnc -display :99 -rfbport 5900 -forever -shared -bg # add -passwd <pw> to require a password ``` Then tell the human how to connect. The container is reachable from the Mac at its OrbStack address -- `orb list` on the Mac gives the name/IP, then `open vnc://<container-ip>:5900` (or `<name>.orb.local:5900`), built-in Screen Sharing. No `-p`, no recreate. (`--cloak-vnc` still exists, but only for plain Docker Desktop, where the container IP is NOT routable from the host and a published loopback port is the only way in. On OrbStack you don't need it -- and it does not have to be set at launch.) **You (the agent) open a headed browser on the display and drive it.** Use a persistent context on the mounted profile so the login survives: ```bash cd "$CLOAK_APP_DIR" node --input-type=module <<'EOF' import { launchPersistentContext } from 'cloakbrowser'; const ctx = await launchPersistentContext({ userDataDir: process.env.DEVA_CLOAK_PROFILE_DIR || '/home/deva/.cloak-profile', headless: false, // renders on :99 so the human sees it over VNC }); const page = ctx.pages()[0] || await ctx.newPage(); await page.goto('https://SITE/login'); // Tell the human: open the printed vnc:// URL and log in (MFA/captcha). // Wait for the post-login page, then continue -- you hold the same context. await page.waitForURL('**/app**', { timeout: 300000 }); console.log('logged in:', await page.title()); await ctx.close(); // profile saved to the mounted host dir EOF ``` The human opens `vnc://127.0.0.1:PORT` (macOS: `open vnc://127.0.0.1:5901`, built-in Screen Sharing), sees your browser, and logs in. Because you hold the same persistent context, the session is yours immediately and -- via the mounted profile -- survives to the next run, even headless with no VNC. **Optional: always-on shared daemon (`--cloak-browser`).** For a browser that stays up across agent runs (so the human can log in even when no agent is active), launch with `--cloak-browser`. deva starts one persistent headed browser and sets `CLOAK_CDP_ENDPOINT`; attach to it instead of launching your own -- do NOT `launch()` a second browser, it would miss the login: ```bash import pw from 'playwright-core'; const b = await pw.chromium.connectOverCDP(process.env.CLOAK_CDP_ENDPOINT); const ctx = b.contexts()[0]; // the daemon's profile, NOT b.newContext() const page = ctx.pages()[0] || await ctx.newPage(); await page.goto('https://SITE/app'); // already logged in await b.close(); // detaches; the daemon browser stays up ``` `CLOAK_CDP_ENDPOINT` is set only when the daemon runs (`--cloak-browser`); its absence means there is no daemon and you should launch your own browser as above. Use `goto()`, not `setContent()`: on a fresh daemon page `setContent` waits for a `load` event that never fires and times out. `page.goto('data:text/html,...', {waitUntil: 'domcontentloaded'})` is the working equivalent for injected markup. **Persistence / what to mount.** `--cloak-vnc` (and `--cloak-browser`) auto-mounts `~/.config/deva/cloak-profile` (host) to the Chromium `userDataDir` `/home/deva/.cloak-profile`. That one dir is the whole session -- cookies, localStorage, IndexedDB, service workers, Cache, History, Login Data. It survives container removal, so a login done once is reused by every later run (even headless, no VNC). Override the host location with `DEVA_CLOAK_PROFILE_DIR=/path deva.sh -p cloak --cloak-vnc ...`, or mount your own dir at `/home/deva/.cloak-profile` (your mount wins). Do NOT mount `/opt/cloakbrowser` -- that is the baked binary, not session data. Gotcha: only cookies with an `expires` survive a profile reopen -- session cookies (no expiry) are memory-only. Real logins persist via the site's long-lived "remember me" / refresh-token cookie, which carries an expiry, so genuine sessions survive (verified). **storageState alternative** (no daemon/VNC): if you captured Playwright storage state elsewhere (cookies + localStorage as plaintext JSON) and mounted the file, load it into a one-off context -- note the nesting: ```bash node --input-type=module <<'EOF' import { launchContext } from 'cloakbrowser'; // storageState MUST be under contextOptions -- launchContext feeds it to // newContext(). Top-level storageState is silently dropped and you get an // UNauthenticated browser (verified against cloakbrowser 0.4.x). const ctx = await launchContext({ headless: false, contextOptions: { storageState: '/home/deva/.cloak-auth/SITE.json' } }); const page = await ctx.newPage(); await page.goto('https://SITE/app'); await ctx.close(); EOF ``` SECURITY -- the profile dir (and any storage-state file) is a live bearer credential: whoever holds it holds the session. This container runs the agent with permissions disabled. VNC is auto-passworded; keep it that way. Only keep the single site the task needs, and never print, log, copy, or write these files back to a shared/primary account. Treat them as secrets. ## CLI (no script needed) ```bash cloakbrowser info # what binary launches, license tier, env checks cloakbrowser info --quick # skip the launch test ``` ## Gotchas - Bare `import 'cloakbrowser'` only resolves when cwd is `$CLOAK_APP_DIR` (Node ESM resolves bare specifiers from cwd for `--input-type=module`). From elsewhere, import the absolute path: `import(process.env.CLOAK_APP_DIR + '/node_modules/cloakbrowser/dist/index.js')`. - Do not run `npx cloakbrowser ...` -- the wrapper is not global; use the `cloakbrowser` command (symlinked) or the `$CLOAK_APP_DIR` binary. - For reCAPTCHA scoring, use native `setTimeout`, not `page.waitForTimeout()` (the latter sends CDP traffic reCAPTCHA detects); use `page.type()` not `page.fill()` so keystroke events fire. - CloakBrowser prevents CAPTCHAs; it does not solve them. Bring your own proxy.
View on GitHub