capiumbrowser 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +674 -117
- package/lib/channels.json +4 -4
- package/lib/download.js +12 -4
- package/lib/license.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,76 @@
|
|
|
1
|
-
<
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/capzy-ai/capiumbrowser/main/assets/capzy-banner.png" alt="Capium — stealth Chromium for Playwright and Puppeteer" width="820">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/capiumbrowser"><img src="https://img.shields.io/npm/v/capiumbrowser?color=cb3837" alt="npm"></a>
|
|
7
|
+
<img src="https://img.shields.io/badge/node-18%2B-brightgreen" alt="Node 18+">
|
|
8
|
+
<img src="https://img.shields.io/badge/drivers-Playwright%20%C2%B7%20Puppeteer-45ba4b" alt="Playwright / Puppeteer">
|
|
9
|
+
<a href="https://docs.capiumbrowser.com"><img src="https://img.shields.io/badge/docs-capiumbrowser.com-6741d9" alt="Docs"></a>
|
|
10
|
+
<a href="https://capiumbrowser.com"><img src="https://img.shields.io/badge/website-capiumbrowser.com-1c7ed6" alt="Website"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<h3 align="center">A stealth Chromium built to withstand bot detection — because it's a real browser.</h3>
|
|
14
|
+
|
|
15
|
+
<table><tr><td>
|
|
16
|
+
Not a patched config. Not a JavaScript injection. A real Chromium binary whose fingerprints are rewritten at the <strong>C++ source level</strong> and compiled in. Page scripts that hunt for injected hooks find nothing to catch — the values a site reads <em>are</em> the persona's values, produced by the engine. Drive it with the <strong>Playwright</strong> or <strong>Puppeteer</strong> API you already know.
|
|
17
|
+
</td></tr></table>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<sub>Say hi to <b>Capzy</b> 🕵️ — the stealth little guy who helps your automation blend in.</sub>
|
|
21
|
+
</p>
|
|
2
22
|
|
|
3
23
|
<p align="center">
|
|
4
24
|
<a href="https://capiumbrowser.com">capiumbrowser.com</a> ·
|
|
5
25
|
<a href="https://docs.capiumbrowser.com">Documentation</a> ·
|
|
6
26
|
<a href="https://capiumbrowser.com/#pricing">Pricing</a> ·
|
|
7
|
-
<a href="https://
|
|
8
|
-
<a href="https://
|
|
27
|
+
<a href="https://capiumbrowser.com/#demo">Demo</a> ·
|
|
28
|
+
<a href="https://github.com/capzy-ai/capiumbrowser/tree/main/examples">Examples</a>
|
|
9
29
|
</p>
|
|
10
30
|
|
|
11
31
|
---
|
|
12
32
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
33
|
+
**`npm install capiumbrowser playwright-core`** (or **`puppeteer-core`**) · CLI: `npx capiumbrowser`
|
|
34
|
+
|
|
35
|
+
> Using Python? The same browser ships as a [PyPI package](https://pypi.org/project/capiumbrowser/) with the same options.
|
|
36
|
+
|
|
37
|
+
- **Source-level C++ stealth** — User-Agent & UA-CH, WebGL/GPU, canvas, audio, screen, hardware, fonts, voices, WebRTC, `navigator.webdriver`, DevTools, pointer, client-rects — all rewritten **in the browser's own source**, below the JavaScript layer.
|
|
38
|
+
- **Coherent per-seed identities** — one integer **seed** derives a complete, self-consistent device (OS, GPU, cores, memory, screen, fonts, UA). Same seed ⇒ same machine, on any host, every run.
|
|
39
|
+
- **`humanize: true`** — attaches `page.humanMove` / `humanClick` / `humanType` / `humanScroll`: curved, human-timed mouse, keyboard, and scroll input.
|
|
40
|
+
- **`geoip: true`** — the binary resolves its own exit IP through your proxy and pins timezone, WebRTC IP, geolocation, and `navigator.languages` to match. The clock, locale, and IP all tell one story.
|
|
41
|
+
- **Plain drivers over CDP** — no patched driver, no init-script signature, no `Object.defineProperty` shim to detect. Update the engine with `npm i -U playwright-core` (or `puppeteer-core`).
|
|
42
|
+
- **Always the latest binary** — one license key fetches the newest checksum-verified build for your platform and reuses it thereafter.
|
|
43
|
+
- **Free tier — 1 concurrent session.** Paid tiers raise the concurrent-session cap. [Start free →](https://capiumbrowser.com)
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const { launchContext } = require('capiumbrowser'); // auto-detects the installed driver
|
|
47
|
+
|
|
48
|
+
const { browser, page } = await launchContext({
|
|
49
|
+
seed: 200123, // a coherent, repeatable device
|
|
50
|
+
proxy: 'http://user:pass@host:port', geoip: true, // exit IP + matching tz/geo/WebRTC
|
|
51
|
+
humanize: true, // human-like clicks/typing/scrolls
|
|
52
|
+
url: 'https://fingerprint.com/demo/',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await page.humanType('#email', 'alex@example.com');
|
|
56
|
+
await page.humanClick('#submit');
|
|
57
|
+
await browser.close();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
> The `capiumbrowser` **npm package is open**; the **Capium browser binary is licensed** per concurrent session. There's a **free tier with 1 concurrent session** — see [Licensing](#licensing).
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Table of contents
|
|
65
|
+
|
|
66
|
+
- [Install](#install) · [Why Capium](#why-capium) · [Licensing & tiers](#licensing)
|
|
67
|
+
- [What a persona reads](#what-a-persona-reads) · [How it works](#how-it-works)
|
|
68
|
+
- [API](#api) · [Options](#options) · [Personas & seeds](#personas--seeds) · [Fingerprint switches](#fingerprint-switches)
|
|
69
|
+
- [Human behavior](#human-behavior) · [Proxies & geo](#proxies--geo) · [Fonts](#fonts)
|
|
70
|
+
- [Framework integrations](#framework-integrations) · [Deployment](#deployment) · [Platforms](#platforms)
|
|
71
|
+
- [CLI](#cli--environment) · [Troubleshooting](#troubleshooting) · [FAQ](#faq) · [Security](#security) · [License](#license)
|
|
72
|
+
|
|
73
|
+
---
|
|
17
74
|
|
|
18
75
|
## Install
|
|
19
76
|
|
|
@@ -25,150 +82,650 @@ npm install capiumbrowser playwright-core
|
|
|
25
82
|
npm install capiumbrowser puppeteer-core
|
|
26
83
|
```
|
|
27
84
|
|
|
28
|
-
The driver is
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
license service (authenticated, checksum-verified) and caches it under `~/.capium`
|
|
32
|
-
(`CAPIUM_HOME` overrides).
|
|
85
|
+
The driver is an **optional peer dependency** — install whichever you use (or both) and update
|
|
86
|
+
it independently. On Linux, install Chromium's OS libraries once:
|
|
87
|
+
`npx playwright-core install-deps chromium` (covers Puppeteer setups too).
|
|
33
88
|
|
|
34
|
-
|
|
89
|
+
You do **not** run `npx playwright install chromium` — Capium ships its own binary. On first
|
|
90
|
+
launch the SDK fetches the per-platform build from the license service (authenticated,
|
|
91
|
+
checksum-verified) and caches it under `~/.capium` (`CAPIUM_HOME` overrides — it deliberately
|
|
92
|
+
lives *outside* `node_modules`, so reinstalls never delete it).
|
|
35
93
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const { launchContext } = require('capiumbrowser');
|
|
94
|
+
**Migrating?** It's a one-line change — the returned object is a standard Playwright /
|
|
95
|
+
Puppeteer `Browser`:
|
|
39
96
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
97
|
+
```diff
|
|
98
|
+
- const { chromium } = require('playwright-core');
|
|
99
|
+
- const browser = await chromium.launch();
|
|
100
|
+
+ const { launch } = require('capiumbrowser/playwright');
|
|
101
|
+
+ const browser = await launch({ seed: 200123 });
|
|
43
102
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
proxy: 'http://user:pass@host:port',
|
|
48
|
-
geoip: true, // exit IP + matching tz/geo/WebRTC
|
|
49
|
-
humanize: true, // human-like clicks/typing/scrolls
|
|
50
|
-
url: 'https://fingerprint.com/demo/',
|
|
51
|
-
});
|
|
103
|
+
const page = await browser.newPage();
|
|
104
|
+
await page.goto('https://example.com'); // the rest of your code is unchanged
|
|
105
|
+
```
|
|
52
106
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
await
|
|
107
|
+
```diff
|
|
108
|
+
- const puppeteer = require('puppeteer');
|
|
109
|
+
- const browser = await puppeteer.launch();
|
|
110
|
+
+ const { launch } = require('capiumbrowser/puppeteer');
|
|
111
|
+
+ const browser = await launch({ seed: 200123 });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> ⭐ **Star** [the repo](https://github.com/capzy-ai/capiumbrowser) to follow along — Capium tracks Chrome closely and ships new builds as detection evolves.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Why Capium
|
|
119
|
+
|
|
120
|
+
- **Config-level stealth breaks.** `puppeteer-extra-plugin-stealth`, patched drivers, and
|
|
121
|
+
friends inject JavaScript or flip flags. Every Chrome update breaks them, and modern
|
|
122
|
+
anti-bot ML detects the patches themselves — the very act of hiding trips the alarm.
|
|
123
|
+
- **Capium patches Chromium source.** Fingerprints are modified at the C++ level and compiled
|
|
124
|
+
into the binary. Detection sites see a real browser because it *is* one — no init script,
|
|
125
|
+
no `toString` tampering, no CDP hooks to sniff.
|
|
126
|
+
- **Coherence is the product.** A convincing device isn't one clean signal; it's *every*
|
|
127
|
+
signal telling the same story. Capium derives GPU, screen, fonts, UA-CH, timezone, locale,
|
|
128
|
+
and WebRTC from a single seed + your proxy's geography, so nothing contradicts anything else.
|
|
129
|
+
- **Same behavior everywhere.** Local, Docker, VPS, Lambda — no environment-specific config.
|
|
130
|
+
- **Works with your stack.** Drop-in stealth for Crawlee, @playwright/test,
|
|
131
|
+
puppeteer-cluster, and anything that speaks CDP. See [integrations](#framework-integrations).
|
|
132
|
+
|
|
133
|
+
Capium doesn't solve CAPTCHAs, and no stealth tool can promise you'll never see one. What a
|
|
134
|
+
coherent, real-browser fingerprint does is remove the automation tells that commonly *trigger*
|
|
135
|
+
challenges — it reduces friction, it doesn't guarantee a challenge-free session. No
|
|
136
|
+
CAPTCHA-solving services, no built-in proxy rotation: bring your own proxies and use the
|
|
137
|
+
driver API you already know.
|
|
138
|
+
|
|
139
|
+
> **Responsible use.** Capium is a stealth browser for legitimate automation, testing, QA, and research.
|
|
140
|
+
> It makes **no guarantee** of evading any particular bot-detection or anti-fraud system, and results vary
|
|
141
|
+
> by site, proxy, and configuration. You are responsible for complying with the terms of service of the
|
|
142
|
+
> sites you access and with all applicable laws.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Licensing
|
|
147
|
+
|
|
148
|
+
The **browser binary is licensed per concurrent session** — each running browser holds one seat, freed the
|
|
149
|
+
moment it closes. Installing or activating on many machines is free; only *live* sessions count, across all
|
|
150
|
+
your machines. The `capiumbrowser` SDK never holds a seat itself.
|
|
151
|
+
|
|
152
|
+
| Tier | Concurrent sessions | For |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| **Free** | 1 | Trying it, one browser at a time |
|
|
155
|
+
| **Solo** | 5 | Solo projects |
|
|
156
|
+
| **Team** | 20 | Small teams |
|
|
157
|
+
| **Business** | 200 | Production scraping / QA / monitoring |
|
|
158
|
+
| **Scale** | 2,000+ | High-volume automation |
|
|
159
|
+
|
|
160
|
+
See **[capiumbrowser.com/#pricing](https://capiumbrowser.com/#pricing)** for current pricing and to create a key.
|
|
161
|
+
|
|
162
|
+
### Get a key
|
|
163
|
+
|
|
164
|
+
1. Create an account at **[capiumbrowser.com](https://capiumbrowser.com)** and create a license (start with the
|
|
165
|
+
free 1-session tier). Keys look like `cap_XXXXXXXXXXXXXXXXXXXXXXXX`.
|
|
166
|
+
2. Make it available to the SDK any one way (checked in this order):
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
export CAPIUM_LICENSE_KEY="cap_xxxxxxxxxxxxxxxxxxxx"
|
|
170
|
+
# optional — defaults to https://license.capzy.ai
|
|
171
|
+
export CAPIUM_LICENSE_SERVER="https://license.capzy.ai"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
…or a file at `~/.capium/license` (chmod 600) with a `KEY=cap_…` line, or pass
|
|
175
|
+
`launch({ licenseKey: 'cap_…' })`.
|
|
176
|
+
|
|
177
|
+
The **same key gates the binary download**. The key is passed to the browser **via the
|
|
178
|
+
environment**, never on the command line (so it can't leak through `ps`). Check entitlements
|
|
179
|
+
without spending a session:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
npx capiumbrowser status
|
|
183
|
+
# plan: free sessions_cap: 1 live_sessions: 0 period_end: ...
|
|
56
184
|
```
|
|
57
185
|
|
|
58
|
-
|
|
59
|
-
|
|
186
|
+
Handle license errors with typed error classes — `CapiumSeatLimitError`, `CapiumExpiredError`,
|
|
187
|
+
`CapiumServerDownError`, `CapiumConfigError` (all subclass `CapiumLicenseError` → `CapiumError`). Full model:
|
|
188
|
+
[docs.capiumbrowser.com](https://docs.capiumbrowser.com).
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## What a persona reads
|
|
193
|
+
|
|
194
|
+
On a **matching-OS host** (a Windows persona on Windows, a macOS persona on macOS), a Capium persona reads
|
|
195
|
+
clean against a modern fingerprinting suite — with the correct GPU, fonts, and voices for the claimed device:
|
|
196
|
+
|
|
197
|
+
| Signal | Stock driver Chromium | Capium |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `navigator.webdriver` | `true` | **not flagged** (source-level) |
|
|
200
|
+
| FingerprintJS **bot detection** | detected | **not detected** |
|
|
201
|
+
| FingerprintJS **tampering / anti-detect** | — | **false** |
|
|
202
|
+
| DevTools/CDP console tell (FPJS `developer_tools`) | detected | **false** |
|
|
203
|
+
| User-Agent | `HeadlessChrome/…` | **`Chrome/152.0.0.0`** |
|
|
204
|
+
| `window.chrome` | thin / missing | **present, like real Chrome** |
|
|
205
|
+
| Google TTS voices | absent | **present (matches real Chrome)** |
|
|
206
|
+
| JA4 TLS fingerprint | differs from Chrome | **matches stock Chrome** |
|
|
207
|
+
| WebRTC IP behind a proxy | real IP leaks | **pinned to the proxy exit** |
|
|
208
|
+
|
|
209
|
+
> **Honesty matters more than a green screenshot.** A **GPU-less headless Linux** box makes WebGL fall back
|
|
210
|
+
> to SwiftShader, which any modern suite (and stock Chrome) flags as `virtual_machine` / `anti_detect` — an
|
|
211
|
+
> artifact of the *host*, not the browser. Run **headed** (Xvfb is fine) on a GPU-backed, matching-OS host
|
|
212
|
+
> for the clean verdict. And `os_mismatch` is a **network** signal — genuine Chrome trips it behind some
|
|
213
|
+
> proxies/NATs too, so treat it as a routing concern, not a browser one.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## How it works
|
|
218
|
+
|
|
219
|
+
1. **You install** → `npm install capiumbrowser playwright-core` (or `puppeteer-core`). This
|
|
220
|
+
downloads **nothing** but the SDK — the ~230 MB binary needs your license key, so it can't
|
|
221
|
+
be fetched at npm-install time.
|
|
222
|
+
2. **First launch** → the license-gated binary auto-downloads for your platform
|
|
223
|
+
(checksum-verified) and caches under `~/.capium`. It's fetched **lazily** on that first
|
|
224
|
+
`launch()` — or eagerly with `npx capiumbrowser install`.
|
|
225
|
+
3. **Every later launch** → the cached binary is reused instantly; the plain driver starts it with your seed.
|
|
226
|
+
4. **Upgrade** → when a newer SDK bumps the target build, the next launch notices the cached
|
|
227
|
+
binary is a different version, **removes it, and downloads the new one** — no manual reinstall.
|
|
228
|
+
|
|
229
|
+
The spoofing lives in the compiled binary — not injected via JavaScript, not set via a detectable CDP
|
|
230
|
+
`Emulation` call. Identity comes from one seed; proxy, geolocation, timezone, WebRTC, and locale are all
|
|
231
|
+
aligned to a single coherent story.
|
|
232
|
+
|
|
233
|
+
**Download mechanics.** The binary is fetched from the license service with a signed, path-based GET
|
|
234
|
+
(`/download/distro/chromium-v<version>/capiumbrowser-<os>-<arch>.tar.gz`): the key travels in the
|
|
235
|
+
`X-Capzy-License` header (never the URL) and the request path is HMAC-signed. The response's
|
|
236
|
+
`X-Capzy-SHA256` is verified against the streamed bytes before extraction, so a corrupted or tampered
|
|
237
|
+
archive is rejected. Env overrides: `CAPIUM_LICENSE_KEY` (or `~/.capium/license`), `CAPIUM_VERSION` (target
|
|
238
|
+
a specific build), `CAPIUM_BINARY` (use an exact local binary, skip download), `CAPIUM_DOWNLOAD_URL`
|
|
239
|
+
(unsigned direct URL escape hatch), `CAPIUM_HOME` (where builds cache). A missing platform, an unpublished
|
|
240
|
+
version (404), a rejected license (401/403), an unreachable server, or a checksum mismatch each throw a
|
|
241
|
+
distinct, typed `Capium*` error.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## API
|
|
246
|
+
|
|
247
|
+
Everything is async. The root import auto-detects the installed driver (Playwright preferred
|
|
248
|
+
when both are present); pick one explicitly via the subpaths:
|
|
249
|
+
|
|
250
|
+
```js
|
|
251
|
+
const capium = require('capiumbrowser'); // auto-detect
|
|
252
|
+
const pw = require('capiumbrowser/playwright'); // Playwright explicitly
|
|
253
|
+
const pptr = require('capiumbrowser/puppeteer'); // Puppeteer explicitly
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
| Function | Returns | Use for |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `launch(options)` | `Browser` | full control; create your own contexts/pages (they default to `viewport: null`) |
|
|
259
|
+
| `launchContext(options)` | `{browser, context?, page}` | one-liner: open a page, optionally navigate + humanize |
|
|
260
|
+
| `launchPersistentContext(dir, options)` | Playwright: `BrowserContext` · Puppeteer: `Browser` | **persistent** cookies/localStorage/login across runs |
|
|
261
|
+
| `buildLaunchOptions(options)` | plain object | hand the driver's own `launch()` (or any framework) the ready-made `{executablePath, args, env, …}` |
|
|
60
262
|
|
|
61
263
|
```js
|
|
62
|
-
// Playwright: launch() -> Browser, launchPersistentContext(dir) -> BrowserContext
|
|
63
264
|
const { launch, launchPersistentContext } = require('capiumbrowser/playwright');
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
WebRTC IP, geolocation, and `navigator.languages` to match.
|
|
83
|
-
- **Plain drivers over CDP** — no patched driver, no init-script signature, no
|
|
84
|
-
`Object.defineProperty` shim to detect.
|
|
85
|
-
- **Always the latest binary** — one license key fetches the newest checksum-verified build for
|
|
86
|
-
your platform and reuses it thereafter.
|
|
87
|
-
|
|
88
|
-
## Options (both drivers)
|
|
89
|
-
|
|
90
|
-
| Option | Default | Meaning |
|
|
91
|
-
| --- | --- | --- |
|
|
92
|
-
| `seed` | random | Identity seed; a stable, coherent device per seed |
|
|
93
|
-
| `platform` | `"windows"` | Spoofed OS persona: `windows` \| `macos` \| `linux` |
|
|
94
|
-
| `headless` | `false` | Windowed is the coherent persona default |
|
|
95
|
-
| `proxy` | none | `"http://user:pass@host:port"`, `"socks5://host:port"`, or `{server, username, password, bypass}` |
|
|
96
|
-
| `geoip` | off | `true` = pin tz/geo/WebRTC/language to the egress; `false` = explicit opt-out |
|
|
97
|
-
| `timezone` / `locale` | — | Explicit overrides (IANA tz, BCP-47 locale; in-binary spoof) |
|
|
98
|
-
| `stealthArgs` | `true` | `false` drops the default fingerprint flags |
|
|
99
|
-
| `licenseKey` / `licenseServer` | env / `~/.capium/license` | Handed to the self-licensing binary via the environment, never argv |
|
|
100
|
-
|
|
101
|
-
Remaining options are forwarded to the driver's own `launch()`.
|
|
102
|
-
|
|
103
|
-
### Framework integrations
|
|
104
|
-
|
|
105
|
-
`buildLaunchOptions()` (on both subpaths) returns the ready-made options object for the
|
|
106
|
-
driver's own `launch()`, for frameworks that must hold the launch themselves (Crawlee,
|
|
107
|
-
custom pools):
|
|
265
|
+
|
|
266
|
+
// Full control
|
|
267
|
+
const browser = await launch({ seed: 200123, platform: 'windows', headless: false });
|
|
268
|
+
|
|
269
|
+
// Proxy + coherent geo (HTTP/HTTPS/SOCKS5, inline creds)
|
|
270
|
+
const b2 = await launch({ seed: 200123, proxy: 'socks5://user:pass@host:port', geoip: true });
|
|
271
|
+
|
|
272
|
+
// Explicit timezone/locale always win over geoip auto-detection
|
|
273
|
+
const b3 = await launch({ seed: 200123, proxy: 'http://host:port', geoip: true, timezone: 'Europe/London' });
|
|
274
|
+
|
|
275
|
+
// Persistent profile — stays logged in, avoids the "always fresh incognito" tell; extensions need this
|
|
276
|
+
const ctx = await launchPersistentContext('profiles/acct1', {
|
|
277
|
+
seed: 200123, platform: 'linux', extensionPaths: ['/path/to/unpacked-extension'],
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Bring your own flags — disable the defaults and build the command line yourself
|
|
281
|
+
const b4 = await launch({ stealthArgs: false, args: ['--fingerprint=200123', '--fingerprint-platform=linux'] });
|
|
282
|
+
```
|
|
108
283
|
|
|
109
284
|
```js
|
|
285
|
+
// Framework escape hatch: you hold the launch, Capium supplies the options
|
|
110
286
|
const { chromium } = require('playwright-core');
|
|
111
287
|
const { buildLaunchOptions } = require('capiumbrowser/playwright');
|
|
112
|
-
const
|
|
288
|
+
const b5 = await chromium.launch(await buildLaunchOptions({ seed: 200123 }));
|
|
113
289
|
```
|
|
114
290
|
|
|
115
|
-
|
|
291
|
+
**Puppeteer specifics** — the same options, with Puppeteer-shaped defaults applied for you:
|
|
292
|
+
`defaultViewport: null` (the page tracks the real window), `--enable-automation` stripped from
|
|
293
|
+
the default args, and persistence via `launch({ userDataDir })` (Puppeteer has no separate
|
|
294
|
+
persistent-context API, so `launchPersistentContext(dir, opts)` is sugar for exactly that).
|
|
116
295
|
|
|
117
|
-
|
|
118
|
-
concurrent session (free tier: 1 concurrent session). Configure the key once:
|
|
296
|
+
---
|
|
119
297
|
|
|
120
|
-
|
|
121
|
-
|
|
298
|
+
## Options
|
|
299
|
+
|
|
300
|
+
Unless noted, all of these work on **`launch`**, **`launchContext`**, and
|
|
301
|
+
**`launchPersistentContext`** (which forward everything to `launch`). Same options as the
|
|
302
|
+
Python SDK, in camelCase.
|
|
303
|
+
|
|
304
|
+
### Identity & rendering
|
|
305
|
+
|
|
306
|
+
| Option | Type | Default | Description |
|
|
307
|
+
|---|---|---|---|
|
|
308
|
+
| `seed` | `number` \| `null` | `null` | Persona seed → a complete coherent device. `null` = fresh random seed each launch. The range selects the OS family (see [Personas & seeds](#personas--seeds)). |
|
|
309
|
+
| `platform` | `string` | `"windows"` | Spoofed OS: `"windows"` \| `"macos"` \| `"linux"`. Sets `--fingerprint-platform` and picks the seed range if you didn't. |
|
|
310
|
+
| `headless` | `boolean` | `false` | Headed is recommended (reads cleaner). On a server, run headed under Xvfb. |
|
|
311
|
+
| `stealthArgs` | `boolean` | `true` | Apply the default coherent fingerprint flags. Set `false` to build the whole command line yourself via `args`. |
|
|
312
|
+
| `args` | `string[]` | — | Extra raw Chrome flags appended verbatim (see [Fingerprint switches](#fingerprint-switches)). |
|
|
313
|
+
|
|
314
|
+
### Proxy, geo & locale
|
|
315
|
+
|
|
316
|
+
| Option | Type | Default | Description |
|
|
317
|
+
|---|---|---|---|
|
|
318
|
+
| `proxy` | `string` \| `object` | — | HTTP/HTTPS/SOCKS5 URL with inline creds, or `{server, username, password, bypass}`. Credentials are URL-encoded and the **binary authenticates natively** (SOCKS5 RFC 1929; preemptive HTTP auth) — commas & special characters are safe. |
|
|
319
|
+
| `geoip` | `boolean` \| `null` | `null` | Geo coherence, resolved inside the binary. **`null`/omitted — OFF**: no lookup runs, even with a proxy (a proxy alone doesn't trigger it, so proxied launches aren't slowed). **`true`** — ON: pins timezone/geo/WebRTC/`navigator.languages` to the egress (proxy exit, or the host IP if proxyless). **`false`** — explicit opt-out. |
|
|
320
|
+
| `timezone` | `string` | — | IANA tz (e.g. `"America/New_York"`) → `--timezone`. **Overrides** `geoip`. |
|
|
321
|
+
| `locale` | `string` | — | BCP-47 (e.g. `"en-US"`) → `--lang` + `--accept-lang`. **Overrides** `geoip`. |
|
|
322
|
+
|
|
323
|
+
### Profiles & extensions
|
|
324
|
+
|
|
325
|
+
| Option | Type | Default | Description |
|
|
326
|
+
|---|---|---|---|
|
|
327
|
+
| `userDataDir` | `string` | — | **`launchPersistentContext` only** (first positional arg). Directory where cookies/localStorage/cache persist across runs. |
|
|
328
|
+
| `extensionPaths` | `string[]` \| `string` | — | Unpacked Chrome extension directory(ies). Extensions require a **persistent context**. |
|
|
329
|
+
|
|
330
|
+
### License
|
|
331
|
+
|
|
332
|
+
| Option | Type | Default | Description |
|
|
333
|
+
|---|---|---|---|
|
|
334
|
+
| `licenseKey` | `string` | — | `cap_…` key. Falls back to `CAPIUM_LICENSE_KEY` env / `~/.capium/license`. Passed to the binary via the environment, never argv. |
|
|
335
|
+
| `licenseServer` | `string` | — | Override the license server (default `https://license.capzy.ai`). |
|
|
336
|
+
| `licenseThroughProxy` | `boolean` | `false` | Route license/session calls through `--proxy-server` instead of direct. |
|
|
337
|
+
| `licensePreflight` | `boolean` | `true` | Fail fast with a typed `CapiumConfigError` when no key is configured, before spinning up a browser. Set `false` for a dev build that runs without enforcement. |
|
|
338
|
+
|
|
339
|
+
### `launchContext` only
|
|
340
|
+
|
|
341
|
+
| Option | Type | Default | Description |
|
|
342
|
+
|---|---|---|---|
|
|
343
|
+
| `url` | `string` | — | If set, navigate the returned page to it (`domcontentloaded`, 60 s) as a driven navigation. |
|
|
344
|
+
| `humanize` | `boolean` | `false` | Attach `page.humanMove` / `humanClick` / `humanType` / `humanScroll` / `humanDwell` (see [Human behavior](#human-behavior)). |
|
|
345
|
+
| `humanPreset` | `string` | `"default"` | Pace of humanized input: `"default"` (natural) or `"careful"` (slower, more deliberate). |
|
|
346
|
+
|
|
347
|
+
### Binary & driver passthrough
|
|
348
|
+
|
|
349
|
+
| Option | Type | Default | Description |
|
|
350
|
+
|---|---|---|---|
|
|
351
|
+
| `binary` | `string` | — | Exact path to the capium launch target — skips discovery/download (same as `CAPIUM_BINARY`). |
|
|
352
|
+
| *(rest)* | — | — | Every remaining option is forwarded to the driver's own `launch()` — `slowMo`, `timeout`, Playwright's `viewport`, Puppeteer's `defaultViewport`/`ignoreDefaultArgs`, etc. Persistent contexts default to `viewport: null` unless you pass one. |
|
|
353
|
+
|
|
354
|
+
> Prefer the friendly options (`proxy`, `geoip`, `timezone`, `locale`) over raw `args` — reach for
|
|
355
|
+
> `args` only when a convenience option doesn't cover the surface. Environment variables that affect
|
|
356
|
+
> every launch are in [CLI & environment](#cli--environment).
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Personas & seeds
|
|
361
|
+
|
|
362
|
+
A **persona** is a complete, self-consistent device deterministically derived from one integer **seed**.
|
|
363
|
+
The seed range selects the OS family (or pass `platform` and Capium picks the range for you):
|
|
364
|
+
|
|
365
|
+
| OS family | Seed range | `platform` |
|
|
366
|
+
|---|---|---|
|
|
367
|
+
| Windows | `10000–99999` | `"windows"` |
|
|
368
|
+
| macOS (Apple Silicon) | `100000–199999` | `"macos"` |
|
|
369
|
+
| Linux | `200000–299999` | `"linux"` |
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
await launch({ seed: 200123 }); // a specific, repeatable device
|
|
373
|
+
await launch({}); // random seed each launch (still fully coherent)
|
|
122
374
|
```
|
|
123
375
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
376
|
+
> **Tip — pin a seed per account.** A fresh random seed every visit looks like a new device each time,
|
|
377
|
+
> which is suspicious when you keep hitting the same site from the same IP. A fixed seed reads as a
|
|
378
|
+
> returning visitor. **And match the persona to the host OS** — a macOS persona is flawless on a Mac and a
|
|
379
|
+
> Windows persona on Windows, because the host's real text-rasterizer and GPU back the claim.
|
|
128
380
|
|
|
129
|
-
|
|
381
|
+
### Launch every persona — and persist per account
|
|
130
382
|
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
383
|
+
```js
|
|
384
|
+
const { launchContext, launchPersistentContext } = require('capiumbrowser');
|
|
385
|
+
|
|
386
|
+
// One coherent device per OS: pick platform + any stable seed
|
|
387
|
+
for (const [platform, seed] of [['windows', 200111], ['macos', 200222], ['linux', 200333]]) {
|
|
388
|
+
const { browser, page } = await launchContext({ seed, platform, url: 'https://browserscan.net/' });
|
|
389
|
+
await browser.close(); // non-persistent: close the browser
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Persist a logged-in account across runs — give it a profile DIRECTORY (persistent-only;
|
|
393
|
+
// launch()/launchContext() are always throwaway).
|
|
394
|
+
const ctx = await launchPersistentContext('capium-profiles/acct-a', { seed: 210001, platform: 'windows' });
|
|
395
|
+
const page = ctx.pages()[0] || (await ctx.newPage());
|
|
396
|
+
await page.goto('https://example.com'); // cookies/localStorage persist in the profile dir
|
|
397
|
+
await ctx.close();
|
|
398
|
+
|
|
399
|
+
// A fleet: each account its own coherent device AND its own profile
|
|
400
|
+
for (const [name, seed] of Object.entries({ 'acct-a': 210001, 'acct-b': 210002, 'acct-c': 210003 })) {
|
|
401
|
+
const c = await launchPersistentContext(`capium-profiles/${name}`, { seed, platform: 'macos' });
|
|
402
|
+
await c.close();
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Full runnable version (personas, persistent profiles, a fleet, proxy+geo, full control):
|
|
407
|
+
[`examples/profiles.js`](https://github.com/capzy-ai/capiumbrowser/tree/main/examples/profiles.js).
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Fingerprint switches
|
|
412
|
+
|
|
413
|
+
The SDK sets the essentials from `seed`/`platform`. Everything below is available for precise control —
|
|
414
|
+
pass any through `args: [...]` (or `stealthArgs: false` to build the whole command line yourself):
|
|
415
|
+
|
|
416
|
+
```js
|
|
417
|
+
await launch({ seed: 200123, args: [
|
|
418
|
+
'--fingerprint-hardware-concurrency=8',
|
|
419
|
+
'--fingerprint-screen-width=2560', '--fingerprint-screen-height=1440',
|
|
420
|
+
]});
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Identity**
|
|
424
|
+
|
|
425
|
+
| Switch | What it controls |
|
|
426
|
+
|---|---|
|
|
427
|
+
| `--fingerprint=<seed>` | Master seed → the whole coherent device. |
|
|
428
|
+
| `--fingerprint-platform=<windows\|macos\|linux>` | Spoofed OS: `navigator.platform`, UA, UA-CH platform. |
|
|
429
|
+
| `--fingerprint-platform-version=<v>` | UA-CH `platformVersion`. |
|
|
430
|
+
| `--fingerprint-brand=<name>` / `--fingerprint-brand-version=<v>` | UA-CH brand and version entries. |
|
|
431
|
+
| `--fingerprint-hardware-concurrency=<n>` | `navigator.hardwareConcurrency` (CPU cores). |
|
|
432
|
+
| `--fingerprint-screen-width=<n>` / `--fingerprint-screen-height=<n>` | `screen.width` / `screen.height`. |
|
|
433
|
+
| `--fingerprint-device-scale-factor=<f>` | `devicePixelRatio`. |
|
|
434
|
+
| `--fingerprint-storage-quota=<GB>` | `navigator.storage.estimate().quota` in **gigabytes** (also affects incognito-from-quota checks). Auto-set per-seed by the SDK (128–576 GB) so the real host/container disk never leaks — including on Windows, where no wrapper runs. |
|
|
435
|
+
|
|
436
|
+
**Rendering & noise**
|
|
437
|
+
|
|
438
|
+
| Switch | What it controls |
|
|
439
|
+
|---|---|
|
|
440
|
+
| `--fingerprint-noise=<true\|false>` | Deterministic per-seed noise on canvas image data, `measureText`, and audio. Default **off** (per-seed noise can read as tampering when the DevTools tell is patched). |
|
|
441
|
+
| `--disable-spoofing=<canvas,font,…>` | Selectively turn off spoofing for named surfaces (comma-separated). |
|
|
442
|
+
|
|
443
|
+
**Fonts & voices**
|
|
444
|
+
|
|
445
|
+
| Switch | What it controls |
|
|
446
|
+
|---|---|
|
|
447
|
+
| `--fingerprint-windows-font-metrics` | Measure default text with Windows metrics (**requires real Windows fonts** — see [Fonts](#fonts)). Default on for a Windows persona. |
|
|
448
|
+
| `--fingerprint-sapi-voices=<true\|false>` | Present the Windows SAPI voice list for a Windows persona. |
|
|
449
|
+
|
|
450
|
+
**Location, network & content**
|
|
451
|
+
|
|
452
|
+
| Switch | What it controls |
|
|
453
|
+
|---|---|
|
|
454
|
+
| `--timezone=<IANA>` | In-browser timezone (e.g. `America/New_York`). |
|
|
455
|
+
| `--fingerprint-location=<lat,lon>` | Geolocation API coordinates (permission auto-granted). |
|
|
456
|
+
| `--fingerprint-webrtc-ip=<ip\|auto>` | The IP WebRTC reports (pin to your proxy exit — the SDK does this with `geoip`). |
|
|
457
|
+
| `--fingerprint-allow-3p-cookies` | Allow third-party cookies (some SSO / embedded-payment flows need them). |
|
|
458
|
+
| `--disable-ads` | Built-in ad/tracker blocker with a login/captcha-safe allowlist (default on). |
|
|
459
|
+
|
|
460
|
+
Most of these have friendly SDK equivalents (`timezone`, `locale`, `proxy`, `geoip`). The full
|
|
461
|
+
per-switch matrix lives in the [docs](https://docs.capiumbrowser.com).
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Human behavior
|
|
466
|
+
|
|
467
|
+
Behavioral anti-bot systems score *how* you move, not just what you send. Pass
|
|
468
|
+
**`humanize: true`** to `launchContext()` (or call `human.humanize(page)` yourself) and human
|
|
469
|
+
input helpers are attached to the page:
|
|
470
|
+
|
|
471
|
+
```js
|
|
472
|
+
const { browser, page } = await launchContext({ seed: 200123, humanize: true, url: 'https://example.com' });
|
|
473
|
+
await page.humanClick('#login'); // curved cursor along a bezier path, then a human press
|
|
474
|
+
await page.humanType('#user', 'alice'); // per-key typing with realistic timing & think-pauses
|
|
475
|
+
await page.humanScroll(1200); // eased, incremental scrolling — not one jump
|
|
476
|
+
await page.humanDwell(); // idle a human-plausible moment
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
- The cursor follows a **curved bezier path** through randomized way-points with variable speed.
|
|
480
|
+
- Keystrokes carry **per-key jitter** with occasional longer think-pauses.
|
|
481
|
+
- Scrolling moves in **eased increments** that decay toward the target.
|
|
482
|
+
|
|
483
|
+
Two presets tune the pace: `"default"` (natural) and `"careful"` (slower). It's pure driver
|
|
484
|
+
input (`page.mouse` / `page.keyboard`) — no CDP tricks — and works with both Playwright and
|
|
485
|
+
Puppeteer pages. Helpers are also importable directly:
|
|
486
|
+
|
|
487
|
+
```js
|
|
488
|
+
const { human } = require('capiumbrowser');
|
|
489
|
+
await human.click(page, '#login');
|
|
490
|
+
await human.typeText(page, '#user', 'alice', 'careful');
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Proxies & geo
|
|
496
|
+
|
|
497
|
+
**No proxy provider and no credentials ship with the package** — you supply your own.
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
await launch({ seed: 200123, proxy: 'http://user:pass@host:port', geoip: true });
|
|
501
|
+
await launch({ seed: 200123, proxy: 'socks5://user:pass@host:port' }); // RFC 1929 auth
|
|
502
|
+
await launch({ proxy: { server: 'http://h:8080', username: 'u', password: 'p', bypass: 'localhost,*.internal' } });
|
|
136
503
|
```
|
|
137
504
|
|
|
138
|
-
|
|
505
|
+
`proxy` accepts an HTTP/HTTPS or SOCKS5 URL (inline credentials) or a
|
|
506
|
+
`{server, username, password, bypass}` object. Credentials are passed **inline** to the Capium
|
|
507
|
+
binary — which authenticates natively (SOCKS5 RFC 1929; preemptive HTTP `Proxy-Authorization`) — and are
|
|
508
|
+
URL-encoded so special characters (commas in `groups-X,country-Y` usernames included) can't truncate the
|
|
509
|
+
proxy string into a real-IP-leaking `DIRECT` fallback.
|
|
139
510
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
| `CAPIUM_BINARY` | Exact launch target; skips discovery and download |
|
|
145
|
-
| `CAPIUM_VERSION` | Pin a specific binary build |
|
|
146
|
-
| `CAPIUM_DOWNLOAD_URL` | Direct `.tar.gz` URL escape hatch (may contain `{platform}`/`{version}`) |
|
|
147
|
-
| `CAPIUM_INLINE_PROXY_AUTH=0` | Legacy CDP proxy-auth path for old binaries |
|
|
511
|
+
Geo coherence is resolved **inside the binary** at launch, which geolocates the egress IP
|
|
512
|
+
and pins timezone, geolocation, WebRTC IP, and `navigator.languages` — so the WebRTC IP equals the
|
|
513
|
+
site-visible IP and the clock/locale match the egress geography. There's no SDK-side network call. `geoip` is
|
|
514
|
+
tri-state:
|
|
148
515
|
|
|
149
|
-
|
|
516
|
+
- **omitted / `null` (default) — OFF.** No geo lookup runs, **even with a proxy** — a proxy alone does not
|
|
517
|
+
trigger it (the lookup is a blocking egress round-trip we don't pay unless you ask). Pass `geoip: true`
|
|
518
|
+
to opt into proxy-exit coherence.
|
|
519
|
+
- **`geoip: true` — ON.** Geolocates the egress and pins timezone/geolocation/WebRTC/`navigator.languages` to
|
|
520
|
+
it — the **proxy exit** when a proxy is set, else the **host's own public IP**.
|
|
521
|
+
- **`geoip: false` — explicit opt-out.** Forwards `--geoip=false` so the in-binary resolver is unambiguously off.
|
|
150
522
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
523
|
+
Explicit `timezone` / `locale` always win (they're set at launch, so they also cover the HTTP
|
|
524
|
+
`Accept-Language` header).
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Fonts
|
|
529
|
+
|
|
530
|
+
Font detection is **measurement-based** — a font only counts as "present" if it genuinely renders with the
|
|
531
|
+
right metrics. Capium controls what actually resolves, gating each persona to a coherent per-seed set and
|
|
532
|
+
hiding the host's other fonts.
|
|
533
|
+
|
|
534
|
+
- **Bundled & free (ship anywhere):** DejaVu, Liberation, and the metric-identical MS clones **Carlito**
|
|
535
|
+
(= Calibri) and **Caladea** (= Cambria).
|
|
536
|
+
- **Proprietary (you supply from a licensed source):** genuine Apple fonts for a macOS persona on a non-Mac
|
|
537
|
+
host, and genuine Microsoft fonts for a Windows persona on a non-Windows host.
|
|
538
|
+
|
|
539
|
+
On a **matching-OS host** the persona uses the operating system's own fonts and there's nothing to install.
|
|
540
|
+
|
|
541
|
+
**Copying Windows fonts to a Linux host** (Windows persona on Linux) — copy the whole font
|
|
542
|
+
folder from any real Windows machine you own; it covers the required set (Segoe UI, Segoe UI
|
|
543
|
+
Light, Calibri, Marlett, MS UI Gothic, Franklin Gothic, Consolas, Courier New) and more:
|
|
154
544
|
|
|
155
545
|
```bash
|
|
156
|
-
#
|
|
546
|
+
# on the Linux box:
|
|
157
547
|
mkdir -p ~/.local/share/fonts/windows
|
|
158
|
-
|
|
548
|
+
# from the Windows machine (PowerShell):
|
|
549
|
+
scp C:\Windows\Fonts\* user@linuxbox:~/.local/share/fonts/windows/
|
|
550
|
+
# back on the Linux box:
|
|
159
551
|
fc-cache -f
|
|
160
|
-
# macOS: drop the .ttf/.ttc files into ~/Library/Fonts/ (picked up immediately)
|
|
161
552
|
```
|
|
162
553
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
554
|
+
The distro also ships **`capium-install-fonts.sh`**, which installs the free baseline
|
|
555
|
+
(emoji/CJK + the Carlito/Caladea/Liberation substitutes) via apt and then checks for the
|
|
556
|
+
required Windows set. Once the fonts resolve, the `capium` wrapper **auto-enables**
|
|
557
|
+
`--fingerprint-windows-font-metrics` — no flag needed.
|
|
558
|
+
|
|
559
|
+
**Copying Windows fonts to a macOS host** — install the same `.ttf`/`.ttc` files via Font
|
|
560
|
+
Book, or drop them into `~/Library/Fonts/` (picked up immediately).
|
|
561
|
+
|
|
562
|
+
**macOS persona on a non-Mac host** — needs genuine Apple fonts (SF Pro, Helvetica Neue, …)
|
|
563
|
+
from a Mac you own, placed in the same font locations. This is the hardest cross-OS
|
|
564
|
+
direction — a macOS persona reads cleanest on a real Mac.
|
|
565
|
+
|
|
566
|
+
Copy proprietary fonts only between machines you hold licenses for. On Linux,
|
|
567
|
+
`CAPIUM_FONTS_DIR` adds a custom font directory; `CAPIUM_SUPPRESS_FONT_WARNING=1` silences the
|
|
568
|
+
"missing Windows fonts" hint once you've supplied them.
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Framework integrations
|
|
573
|
+
|
|
574
|
+
Capium is a Chromium fork, so any Chromium automation tool works — point it at the binary and add the
|
|
575
|
+
persona flags. Runnable examples live in
|
|
576
|
+
[`examples/integrations/`](https://github.com/capzy-ai/capiumbrowser/tree/main/examples/integrations/):
|
|
577
|
+
|
|
578
|
+
| Framework | Example |
|
|
579
|
+
|---|---|
|
|
580
|
+
| [Crawlee](https://github.com/apify/crawlee) (`PlaywrightCrawler`) | [`crawlee.js`](https://github.com/capzy-ai/capiumbrowser/tree/main/examples/integrations/crawlee.js) |
|
|
581
|
+
| [@playwright/test](https://playwright.dev/docs/intro) | [`playwright_test.spec.js`](https://github.com/capzy-ai/capiumbrowser/tree/main/examples/integrations/playwright_test.spec.js) |
|
|
582
|
+
| [puppeteer-cluster](https://github.com/thomasdondorf/puppeteer-cluster) | [`puppeteer_cluster.js`](https://github.com/capzy-ai/capiumbrowser/tree/main/examples/integrations/puppeteer_cluster.js) |
|
|
583
|
+
| any CDP tool | [`cdp_connect.js`](https://github.com/capzy-ai/capiumbrowser/tree/main/examples/integrations/cdp_connect.js) |
|
|
584
|
+
|
|
585
|
+
Two patterns cover them all:
|
|
586
|
+
|
|
587
|
+
```js
|
|
588
|
+
// 1) Framework launches our binary directly (Crawlee, puppeteer-cluster, @playwright/test):
|
|
589
|
+
// buildLaunchOptions() returns {executablePath, args, env, ...} for the framework's launcher.
|
|
590
|
+
const { buildLaunchOptions } = require('capiumbrowser/playwright'); // or /puppeteer
|
|
591
|
+
const launchOptions = await buildLaunchOptions({ seed: 200123, platform: 'windows' });
|
|
592
|
+
|
|
593
|
+
// 2) Capium launches first, the framework connects over CDP (AI agents, remote tools):
|
|
594
|
+
const { launch } = require('capiumbrowser/playwright');
|
|
595
|
+
const browser = await launch({ seed: 200123, geoip: true, args: ['--remote-debugging-port=9242'] });
|
|
596
|
+
// point anything at http://127.0.0.1:9242 — all stealth flags are already set
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
601
|
+
## Deployment
|
|
602
|
+
|
|
603
|
+
Capium runs identically local, in Docker, and on a VPS. Build your own image — download the
|
|
604
|
+
binary at build time with a BuildKit secret so the key never lands in a layer, and keep it
|
|
605
|
+
under `CAPIUM_HOME` so `npm ci` can never delete it:
|
|
606
|
+
|
|
607
|
+
```dockerfile
|
|
608
|
+
FROM node:22-slim
|
|
609
|
+
ENV CAPIUM_HOME=/opt/capium
|
|
610
|
+
RUN npx -y playwright-core install-deps chromium
|
|
611
|
+
WORKDIR /app
|
|
612
|
+
COPY package*.json ./
|
|
613
|
+
RUN npm ci # capiumbrowser + playwright-core (or puppeteer-core) from your package.json
|
|
614
|
+
RUN --mount=type=secret,id=capium_license \
|
|
615
|
+
CAPIUM_LICENSE_KEY="$(cat /run/secrets/capium_license)" npx capiumbrowser install
|
|
616
|
+
COPY your_script.js .
|
|
617
|
+
CMD ["node", "your_script.js"]
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
```bash
|
|
621
|
+
DOCKER_BUILDKIT=1 docker build --secret id=capium_license,env=CAPIUM_LICENSE_KEY -t my-capium .
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
On a headless Linux server, run the browser under a virtual display (`Xvfb :99` + `DISPLAY=:99`) so it
|
|
625
|
+
launches **headed**, which reads cleaner than headless.
|
|
626
|
+
|
|
627
|
+
---
|
|
167
628
|
|
|
168
629
|
## Platforms
|
|
169
630
|
|
|
170
|
-
|
|
631
|
+
| Platform | Distro tag | Notes |
|
|
632
|
+
|---|---|---|
|
|
633
|
+
| Windows x86_64 | `windows-x64` | Native GPU/fonts back a Windows persona flawlessly |
|
|
634
|
+
| macOS arm64 (Apple Silicon) | `macos-arm64` | M1–M4 by design (covers 2020+ Macs); a Windows persona uses installed Windows fonts for fallback |
|
|
635
|
+
| Linux x86_64 | `linux-x64` | Defaults to a Windows persona; supply Windows fonts for the cleanest result |
|
|
636
|
+
| Linux arm64 | `linux-arm64` | Same as Linux x64 for aarch64 hosts |
|
|
637
|
+
|
|
638
|
+
The SDK auto-detects your host and downloads the matching tag; macOS Intel and Windows ARM are **not**
|
|
639
|
+
published and throw a clear error. Capium currently targets **Chromium 152**.
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
## CLI & environment
|
|
644
|
+
|
|
645
|
+
```bash
|
|
646
|
+
npx capiumbrowser # environment check: license + binary + drivers ("what do I still need?")
|
|
647
|
+
npx capiumbrowser status # license entitlements (plan, seats, live sessions, renewal)
|
|
648
|
+
npx capiumbrowser version # SDK version + the Chromium build it targets
|
|
649
|
+
npx capiumbrowser install # download the capium browser binary
|
|
650
|
+
SEED=200123 PLATFORM=linux PROXY="http://user:pass@host:port" GEOIP=1 HUMANIZE=1 \
|
|
651
|
+
npx capiumbrowser run https://example.com # launch a demo browser (Ctrl-C to exit)
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
`npx capiumbrowser` (bare, = `info`) checks the drivers, the binary (path + version), and the
|
|
655
|
+
license key; it prints a fix for anything missing and exits non-zero if the environment isn't ready.
|
|
656
|
+
|
|
657
|
+
| Variable | Purpose |
|
|
658
|
+
|---|---|
|
|
659
|
+
| `CAPIUM_LICENSE_KEY` / `CAPIUM_LICENSE_SERVER` | license key (`cap_…`) + server (default `https://license.capzy.ai`) |
|
|
660
|
+
| `CAPIUM_BINARY` | exact path to the capium launch target (skips download) |
|
|
661
|
+
| `CAPIUM_DOWNLOAD_URL` | direct archive URL (`{platform}`/`{version}` placeholders); unsigned escape hatch |
|
|
662
|
+
| `CAPIUM_HOME` / `CAPIUM_VERSION` | where distros cache (default `~/.capium`) / pin a binary version |
|
|
663
|
+
| `CAPIUM_INLINE_PROXY_AUTH=0` | legacy CDP proxy-auth path (only for a binary lacking the preemptive-auth patch) |
|
|
664
|
+
| `CAPIUM_FONTS_DIR` / `CAPIUM_SUPPRESS_FONT_WARNING` | custom font dir / silence the font hint (Linux) |
|
|
665
|
+
|
|
666
|
+
---
|
|
667
|
+
|
|
668
|
+
## Troubleshooting
|
|
669
|
+
|
|
670
|
+
| Symptom | Fix |
|
|
671
|
+
|---|---|
|
|
672
|
+
| `capium binary not found` | Set `CAPIUM_LICENSE_KEY` (to auto-download), or `CAPIUM_BINARY`, or run `npx capiumbrowser install`. |
|
|
673
|
+
| `no license configured` | Set `CAPIUM_LICENSE_KEY`, pass `licenseKey: '…'`, or create `~/.capium/license` (`KEY=cap_…`). |
|
|
674
|
+
| `playwright-core is not installed` | `npm install playwright-core` (or `puppeteer-core`) — the driver is a peer dependency you choose. |
|
|
675
|
+
| `CapiumSeatLimitError` | Free tier is 1 concurrent session — close the other Capium browser (or upgrade the tier). |
|
|
676
|
+
| Still blocked on aggressive sites | Run **headed** (`headless: false` under Xvfb) + a **residential** proxy + `geoip: true` + humanized input. Most blocks are IP reputation or a mismatched clock, not fingerprint. |
|
|
677
|
+
| Intermittent 403s on reCAPTCHA Enterprise sites | Prefer the **Playwright** front-end — Puppeteer's CDP protocol usage leaks more automation signals (a Puppeteer limitation, not a Capium one). |
|
|
678
|
+
| FPJS `tampering: true` | Cross-OS persona on a mismatched host, or per-seed noise unmasked — run on a matching-OS host; `--fingerprint-noise=false` is the default. |
|
|
679
|
+
| "Windows persona but missing fonts" | Supply real `C:\Windows\Fonts\*.ttf` (see [Fonts](#fonts)) or set `CAPIUM_SUPPRESS_FONT_WARNING=1`. |
|
|
680
|
+
| `os_mismatch: true` | A network/transport signal (genuine Chrome shows it too behind some proxies) — a routing concern, not the browser. |
|
|
681
|
+
| Headed browser won't start on a server | Run under `Xvfb` and export `DISPLAY=:99`. |
|
|
682
|
+
|
|
683
|
+
More: [docs.capiumbrowser.com](https://docs.capiumbrowser.com).
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
|
|
687
|
+
## FAQ
|
|
688
|
+
|
|
689
|
+
**Is this legal?** Capium is a browser built on open-source Chromium. Use it only where you're authorized to.
|
|
690
|
+
Automating systems without authorization, credential stuffing, and account-creation abuse are prohibited.
|
|
691
|
+
|
|
692
|
+
**Is it free?** The `capiumbrowser` npm package is open. The binary has a **free tier with 1 concurrent
|
|
693
|
+
session** using the same latest build; paid tiers raise the concurrent-session cap. See
|
|
694
|
+
[capiumbrowser.com](https://capiumbrowser.com/#pricing).
|
|
695
|
+
|
|
696
|
+
**Do I need a license key?** Yes — the binary is license-gated and fails closed. A free key (from the
|
|
697
|
+
dashboard) gives one concurrent session; a paid key raises the limit.
|
|
698
|
+
|
|
699
|
+
**Playwright or Puppeteer?** Both are first-class. Playwright is the recommended default (its native
|
|
700
|
+
launch path is the one measured cleanest, and it matters on reCAPTCHA-Enterprise-heavy sites);
|
|
701
|
+
Puppeteer works great when your stack is already built on it.
|
|
702
|
+
|
|
703
|
+
**How is this different from JS stealth plugins?** They inject JavaScript that modern ML detects. Capium
|
|
704
|
+
rewrites fingerprints in the Chromium source and compiles them in — there's no injected hook to catch.
|
|
705
|
+
|
|
706
|
+
**Will detection eventually catch this?** Bot detection is an arms race. Source-level patches are much harder
|
|
707
|
+
to detect than config-level ones, and Capium rebases onto new Chromium and ships updates as detection evolves.
|
|
708
|
+
|
|
709
|
+
**Can I use my own proxy?** Yes — HTTP/HTTPS and SOCKS5 are supported natively. Bring your own.
|
|
710
|
+
|
|
711
|
+
**Does this work with Python?** Yes — the same browser ships as a
|
|
712
|
+
[PyPI package](https://pypi.org/project/capiumbrowser/) with the same options (snake_case).
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## Security
|
|
717
|
+
|
|
718
|
+
The SDK fetches the binary over an **authenticated** request — the license key travels in a header (over
|
|
719
|
+
TLS), never in the URL, and the request target is HMAC-signed. The response's checksum is
|
|
720
|
+
verified against the downloaded bytes before anything is extracted, so a corrupted or tampered download is
|
|
721
|
+
rejected. The key is handed to the browser subprocess through the **environment**, never on argv.
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
## License
|
|
726
|
+
|
|
727
|
+
- **`capiumbrowser` npm package** — open; see [LICENSE](https://github.com/capzy-ai/capiumbrowser/blob/main/LICENSE).
|
|
728
|
+
- **Capium browser binary** (compiled Chromium) — licensed per concurrent session; requires an active key
|
|
729
|
+
to download and run. Free tier available. See [capiumbrowser.com](https://capiumbrowser.com).
|
|
171
730
|
|
|
172
|
-
|
|
173
|
-
> research. Capium doesn't solve CAPTCHAs, and no stealth tool can promise you'll never see
|
|
174
|
-
> one. Respect the terms of service of the sites you automate.
|
|
731
|
+
<p align="center"><sub><a href="https://capiumbrowser.com">Capium Browser</a> is a product of <a href="https://capzy.ai">Capzy</a>. Use responsibly and only where you are authorized to.</sub></p>
|
package/lib/channels.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "SINGLE SOURCE OF TRUTH for the current stable Capium browser build per OS/arch. Each OS reaches stable independently -- edit one entry without touching the others. This file ships inside the wheel (the SDK reads it to pick each host's stable build) AND is published to the download server by the release pipeline (so the server/website can resolve 'latest stable' without a new SDK release). `version` is the Chromium base of the immutable artifact folder; `revision` is the cap<N> re-spin counter. `published: false` means the tag is declared but no artifact exists yet -- the SDK raises a clear 'not published yet' error instead of a raw 404.",
|
|
3
3
|
"schema": 1,
|
|
4
|
-
"updated": "2026-09-
|
|
4
|
+
"updated": "2026-09-02",
|
|
5
5
|
"channels": {
|
|
6
6
|
"stable": {
|
|
7
|
-
"windows-x64": { "version": "152.0.7977.65", "revision":
|
|
8
|
-
"macos-arm64": { "version": "152.0.7977.65", "revision":
|
|
9
|
-
"linux-x64": { "version": "152.0.7977.64", "revision":
|
|
7
|
+
"windows-x64": { "version": "152.0.7977.65", "revision": 2, "sha256": "eec6d63a8a1ae7371388a39d10506117261131768020f15aa2d281d9931d35c0", "published": true },
|
|
8
|
+
"macos-arm64": { "version": "152.0.7977.65", "revision": 2, "sha256": "350e7f87b8f8e7e32feee26b8844e80b4650c9aa5fb6888fbcf4cc19fb08eda9", "published": true },
|
|
9
|
+
"linux-x64": { "version": "152.0.7977.64", "revision": 2, "sha256": "248e735b3afec58a44e962cd866f704adfb15ee8b71cf976665dd109f512b7c5", "published": true },
|
|
10
10
|
"linux-arm64": { "version": "152.0.7977.64", "revision": 1, "sha256": null, "published": false }
|
|
11
11
|
}
|
|
12
12
|
}
|
package/lib/download.js
CHANGED
|
@@ -41,9 +41,10 @@ const tar = require('tar');
|
|
|
41
41
|
const config = require('./config');
|
|
42
42
|
const license = require('./license');
|
|
43
43
|
const { CapiumError, CapiumExpiredError, CapiumServerDownError } = require('./errors');
|
|
44
|
-
const { binaryVersionFor, isPublished } = require('./version');
|
|
44
|
+
const { SDK_VERSION, binaryVersionFor, isPublished } = require('./version');
|
|
45
45
|
|
|
46
46
|
const NET_TIMEOUT_MS = 300000;
|
|
47
|
+
const USER_AGENT = `capiumbrowser/${SDK_VERSION}`;
|
|
47
48
|
|
|
48
49
|
// (normalized os, normalized arch) -> distro tag used in the filename/URL.
|
|
49
50
|
const SUPPORTED = {
|
|
@@ -86,8 +87,11 @@ function downloadTag(system = null, machine = null) {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
/**
|
|
89
|
-
* The signed request path for a build
|
|
90
|
-
*
|
|
90
|
+
* The signed request path for a build: version in the folder, tag in the filename
|
|
91
|
+
* (`/download/distro/chromium-v<version>/capiumbrowser-<tag>.tar.gz`). Per-OS versions work
|
|
92
|
+
* because each tag lives under its own chromium-v<version>/ folder (win/mac .65 while linux
|
|
93
|
+
* is .64). Key travels in the X-Capzy-License header, the path is HMAC-signed, and the bytes
|
|
94
|
+
* are verified against the response's X-Capzy-SHA256.
|
|
91
95
|
*/
|
|
92
96
|
function distroPath(version, tag) {
|
|
93
97
|
return `/download/distro/chromium-v${version}/capiumbrowser-${tag}.tar.gz`;
|
|
@@ -216,8 +220,12 @@ function removeInstalls(root) {
|
|
|
216
220
|
*/
|
|
217
221
|
async function download(url, headers, dest, version, tag) {
|
|
218
222
|
let res;
|
|
223
|
+
// Identify the SDK on every request. undici otherwise sends a default UA that Cloudflare's
|
|
224
|
+
// Browser Integrity Check rejects as a bot (403) before the request reaches the origin. A
|
|
225
|
+
// stable product UA is both correct hygiene and what the edge allows.
|
|
226
|
+
const h = Object.assign({ 'User-Agent': USER_AGENT }, headers || {});
|
|
219
227
|
try {
|
|
220
|
-
res = await fetch(url, { headers:
|
|
228
|
+
res = await fetch(url, { headers: h, signal: AbortSignal.timeout(NET_TIMEOUT_MS) });
|
|
221
229
|
} catch (e) {
|
|
222
230
|
throw new CapiumServerDownError(
|
|
223
231
|
`could not reach the download server ${url}: ${e.cause?.message || e.message || e}`);
|
package/lib/license.js
CHANGED
|
@@ -14,8 +14,10 @@ const os = require('os');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
|
|
16
16
|
const { CapiumConfigError, CapiumExpiredError, CapiumServerDownError } = require('./errors');
|
|
17
|
+
const { SDK_VERSION } = require('./version');
|
|
17
18
|
|
|
18
19
|
const DEFAULT_SERVER = 'https://license.capzy.ai';
|
|
20
|
+
const USER_AGENT = `capiumbrowser/${SDK_VERSION}`;
|
|
19
21
|
|
|
20
22
|
function licenseFile() {
|
|
21
23
|
// CAPIUM_LICENSE_FILE is a test/deploy override; the contract path is ~/.capium/license.
|
|
@@ -167,6 +169,7 @@ async function status(key = null, server = null) {
|
|
|
167
169
|
'X-Capzy-Device-Fp': fp,
|
|
168
170
|
'X-Capzy-Signature': sig,
|
|
169
171
|
'Content-Type': 'application/json',
|
|
172
|
+
'User-Agent': USER_AGENT,
|
|
170
173
|
},
|
|
171
174
|
signal: AbortSignal.timeout(10000),
|
|
172
175
|
});
|
package/package.json
CHANGED