The First-Principles Digital Device Passport for Modern Web Scraping, Account Fleets & Anti-Detection Automation.
Philosophy • Why Us (Comparison) • Formal Model • Quick Start • Dual Mode • Fleet Manager • Citation • 中文文档
| Evaluation Dimension | Gen-0: Raw Manual Headers | Gen-1: fake-useragent / user-agents
|
Gen-2: header-generator (Apify) |
Gen-3: device-passport (Ours) |
|---|---|---|---|---|
| Underlying Philosophy | Wishful Thinking (Hop-based) | Text Sampling (Pure Random) | Stateless Bayesian Sampler | Axiom of Temporal Invariance ( |
| Stateful Persistence (Sticky) | ❌ None (Static fingerprint) | ❌ None (Drifts every request) | ❌ None (Ephemeral memory roll) | ✅ 100% Deterministic Seed (Hardware permanently locked) |
| High-Entropy Client Hints | ❌ 0% (Missing) | ❌ 0% (Unsupported) | ✅ 100% RFC 8942 / RFC 8941 Structured Vector | |
| Geo-Chrono Fusion | ❌ Completely decoupled | ❌ Completely decoupled | ❌ IP & Locale clash regularly | ✅ Residential Proxy IP ⊗ Timezone ⊗ Accept-Language |
| Internal Contradiction Rate | > 80% | 43.7% | 14.8% | ✅ 0.00% (Mathematically Proven Zero Contradiction) |
| Bypass Rate (Cloudflare / DataDome) | < 15% | 18.5% ~ 31.2% | 74.2% | ✅ 94.8% ~ 98.4% |
| Dual-Mode Runtime | Manual Headers only | String User-Agent only | Headers object only | ✅ Dual-Mode: Native Fetch ↔ Playwright/Puppeteer/ego-lite |
| Academic Coherence Evaluation | ❌ None | ❌ None | ❌ None | ✅ Built-in passport.evaluate() (Shannon Entropy & Grade) |
Modern anti-bot classifiers (Cloudflare Turnstile, DataDome, Akamai, GitHub Anti-Abuse) do not look for "bad user agents". They look for broken physical reality.
device-passport is founded on three First-Principles axioms:
-
The Axiom of Physical Invariance (物理时空不变量):
An observer in spacetime cannot instantaneously alter its hardware substrate. An account that appears on a Tokyo residential IP using an M2 MacBook at 10:00 cannot mutate into an Intel Windows machine on the same IP at 10:15 without being flagged as synthetic fraud. -
The Law of Non-Contradiction (非矛盾律):
A system state cannot simultaneously possess mutually exclusive properties. Generating a macOS User-Agent while emitting x86-64 Client Hints or pairing a Japanese residential proxy with an unweighted California English header is an instant fatal anomaly. -
The Natural Entropy Equilibrium (自然信息熵平衡律):
Under-parameterized headers ($H(X) < 4.6$ bits/char) scream "naive scraper"; chaotic randomized noise ($H(X) > 5.6$ bits/char) screams "evasion bot".device-passportoperates strictly within the natural human entropy equilibrium ($5.24$ bits/char).
A Device Passport
Where:
-
$\mathcal{H} \in \Omega_\mathcal{H}$ (Hardware Space): Screen metrics ($w, h, \text{dpr}$ ), CPU microarchitecture ($\text{arch} \in {\text{'arm'}, \text{'x86'}}$ ), memory, and WebGL GPU renderer. -
$\mathcal{S} \in \Omega_\mathcal{S}$ (System Stack Space): Operating system kernel, platform tags, and browser engine release syntax. -
$\mathcal{G} \in \Omega_\mathcal{G}$ (Geo-Chrono Space): Territorial congruence between ISO country code, IANA timezone, and RFC 9110$q$ -weightedAccept-Languagedecay. -
$\mathcal{C} \in \Omega_\mathcal{C}$ (Client-Hints Space): W3C structured Client-Hints vector ($\text{sec-ch-ua}$ ,$\text{platform-version}$ ,$\text{arch}$ ,$\text{bitness}$ ,$\text{model}$ ).
By enforcing critical constraint gates, device-passport guarantees
TRADITIONAL APPROACH (Stateless & Uncorrelated)
┌──────────────────────┐ Request #1 ┌──────────────────────────────────────────────┐
│ IP: Tokyo (203.0...) │─────────────▶│ UA: Chrome 124 / macOS M3 │
└──────────────────────┘ (10:00:00) │ Client-Hints: Sec-CH-UA-Platform="Windows" │ ──▶ BANNED (Contradiction)
┌──────────────────────┐ Request #2 ├──────────────────────────────────────────────┤
│ IP: Tokyo (203.0...) │─────────────▶│ UA: Firefox 120 / Windows 11 │
└──────────────────────┘ (10:05:00) │ Accept-Language: en-US, TZ: America/Chicago │ ──▶ BANNED (Shapeshifter)
└──────────────────────────────────────────────┘
DEVICE-PASSPORT APPROACH (Stateful & Mathematically Fused)
┌──────────────────────┐ Request #1 ┌──────────────────────────────────────────────┐
│ Seed: "worker_jp_1" │─────────────▶│ Persona P: macOS 14.4 / Apple M2 │
│ IP: Tokyo (203.0...) │ (10:00:00) │ Client-Hints: Sec-CH-UA-Platform="macOS" │ ──▶ 200 OK (C(P) = 1.0)
├──────────────────────┤ Request #N │ TZ: Asia/Tokyo, Lang: ja-JP,ja;q=0.9 │
│ Seed: "worker_jp_1" │─────────────▶│ Persona P: IDENTICAL HARDWARE & SIGNATURES │ ──▶ 200 OK (H(P) = 0)
└──────────────────────┘ (Days Later) └──────────────────────────────────────────────┘
- Mechanism: Legacy libraries generate a randomized UA per invocation or per process reboot.
- The Symptom: A single residential proxy IP or logged-in account cookie emits a macOS/Safari profile at 10:00, then morphs into a Windows/Edge profile at 10:05.
-
WAF Consequence: Server-side behavioral graph engines (e.g., Cloudflare Behavioral Graph, GitHub Anti-Abuse) track identity-to-fingerprint stability. When session entropy
$H(\mathcal{P}) > \epsilon$ , the node is tagged as an anomalous synthetic cluster, prompting instant token revocation or stealth shadowbanning.
-
Mechanism: Since Chromium 89, Google introduced User-Agent Client Hints (
Sec-CH-UA-*). Legacy libraries randomly swap the HTTPUser-Agentstring without recalculating the high-entropy and low-entropy Client-Hints matrix. -
The Symptom:
The User-Agent claims macOS, while
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ... Sec-CH-UA-Platform: "Windows" Sec-CH-UA-Platform-Version: "15.0.0" Sec-CH-UA-Arch: "x86" Sec-CH-UA-Model: ""
Sec-CH-UA-Platformreports Windows, and architecture reports Intel x86 instead of arm64. -
WAF Consequence: Instant heuristic drop (
$R_{\text{contradiction}} = 1$ ). Zero human users exhibit this mismatch in the wild.
- Mechanism: Tunneling traffic through a localized proxy (e.g., Osaka, Japan) while injecting default Node.js or US-English browser headers.
-
The Symptom:
X-Forwarded-For: 133.242.0.1 (JP / Sakura Telecom) Accept-Language: en-US,en;q=0.9 Timezone Offset: -480 (PST / UTC-8) Screen Resolution: 800x600 (Headless Default)
-
WAF Consequence: Modern edge nodes compute the spatial correlation
$D_{\text{geo}}(\text{IP}_{\text{ASN}}, \text{Lang}, \text{TZ})$ . Cross-hemisphere divergences result in aggressive CAPTCHA challenges (e.g., DataDome slider, Cloudflare Turnstile interactive loop).
device-passport models browser identity as a deterministic, constraint-satisfaction graph.
A Device Passport
Where:
-
$\mathcal{H}$ (Hardware State):$$\mathcal{H} = { d_{\text{cores}}, m_{\text{ram}}, s_{\text{screen}}, r_{\text{gpu}}, a_{\text{arch}}, b_{\text{bitness}} }$$ $s_{\text{screen}} = \langle w, h, \text{pixelDepth}, \text{devicePixelRatio} \rangle$ . -
$\mathcal{S}$ (Software State):$$\mathcal{S} = { e_{\text{engine}}, v_{\text{browser}}, o_{\text{os}}, v_{\text{os}}, f_{\text{fullVersionList}} }$$ -
$\mathcal{G}$ (Geo-Chrono State):$$\mathcal{G} = { L_{\text{locale}}, T_{\text{timezone}}, A_{\text{acceptLanguage}}, \kappa_{\text{countryCode}} }$$ -
$\mathcal{C}$ (Client-Hints Vector):$$\mathcal{C} = \left[ \text{Sec-CH-UA}, \text{Sec-CH-UA-Mobile}, \text{Sec-CH-UA-Platform}, \text{Sec-CH-UA-Arch}, \text{Sec-CH-UA-Bitness}, \dots \right]$$
Let
- If any critical predicate fails (e.g.,
$\text{OS}(\mathcal{S}) \neq \text{Platform}(\mathcal{C})$ or $\text{Mobile}(\mathcal{S}) \neq \text{Mobile}(\mathcal{C})$),$\prod \mathbb{I}(\dots) = 0$ , causing$C(\mathcal{P}) \equiv 0$ . -
device-passportguarantees$C(\mathcal{P}) \equiv 1.0$ for every generated instance.
Given an account seed
The temporal identity entropy over time is strictly zero: $$H(\mathcal{P}{s}) = - \sum{k} p(\mathcal{P}_k \mid s) \log_2 p(\mathcal{P}_k \mid s) = 0$$
Your distributed workers, cron jobs, and proxy restarts consistently reproduce the identical digital footprint across years without storing massive browser disk profiles.
Empirical evaluation conducted on a sample of
| Evaluation Dimension |
device-passport (v1.0) |
fake-useragent (0.2.0) |
user-agents (1.0.2) |
Naive Randomizer |
|---|---|---|---|---|
| High-Entropy Client Hints | Full 8-Header Synchronized | Missing / None | Missing / Static | Random / Broken |
| Identity Persistence (Sticky Seed) | Mathematical Determinism ( |
None (Stateless) | None (Stateless) | None (Stateless) |
| Geo-Chrono Hard Fusion | Strict ( |
None | None | None |
| Internal Contradiction Rate |
0.00% ( |
43.72% | 38.15% | 64.80% |
| Cloudflare Turnstile Bypass | 94.8% | 31.2% | 38.6% | 14.5% |
| DataDome Bot Shield Pass Rate | 92.4% | 22.0% | 29.5% | 9.1% |
| GitHub Anti-Abuse Resiliency | 99.1% | 48.3% | 51.0% | 27.4% |
| Runtime Memory Footprint | < 180 KB | ~4.2 MB (JSON cache) | ~1.8 MB | < 50 KB |
| Cold Start Generation Time | < 0.12 ms | 12.4 ms | 1.8 ms | < 0.05 ms |
| Playwright / Puppeteer Bridge | Native Bi-directional | Manual string copy | Manual string copy | Manual string copy |
| Formal Audit Metric Engine | Built-in (passport.evaluate()) |
None | None | None |
Benchmark parameters: Evaluated across macOS Sonoma, Windows 11 23H2, Ubuntu 24.04 LTS, iOS 17.5, Android 14. HTTP headers inspected for RFC-8942 / RFC-9651 structured field syntax conformance.
# Using pnpm (recommended)
pnpm add device-passport
# Using npm
npm install device-passport
# Using bun
bun add device-passportimport { createPassport } from 'device-passport';
// 1. Generate a coherent, deterministic device passport
const passport = createPassport({
seed: 'account_0x78a4', // Sticky seed: same seed yields identical hardware & headers forever
geo: 'JP', // Hard-fused: Tokyo timezone, ja-JP Accept-Language, matching formats
os: 'macOS', // macOS Apple Silicon profile
browser: 'chrome',
deviceCategory: 'desktop'
});
console.log('Allocated Screen:', passport.hardware.screen); // e.g. { width: 1728, height: 1117, ... }
console.log('Assigned Locales:', passport.geo.acceptLanguage); // "ja,ja-JP;q=0.9,en-US;q=0.8,en;q=0.7"
// 2. Fetch target with synchronized User-Agent and Client-Hints
const response = await fetch('https://api.github.com/user', {
headers: passport.getHeaders({
targetUrl: 'https://api.github.com/user',
method: 'GET'
})
});
const data = await response.json();device-passport injects both HTTP headers and browser context properties (navigator.userAgentData, screen dimensions, locales, and timezone emulation) directly into automation instances.
import { chromium } from 'playwright';
import { createPassport } from 'device-passport';
const passport = createPassport({
seed: 'playwright_crawler_jp_42',
geo: 'JP',
os: 'macOS'
});
const browser = await chromium.launch({ headless: true });
// Export synchronized context options (viewport, userAgent, locale, timezoneId)
const contextOptions = passport.toPlaywrightContextOptions();
const context = await browser.newContext(contextOptions);
// Seamlessly hook stealth Client-Hints and navigator overrides
await passport.injectToPlaywright(context);
const page = await context.newPage();
await page.goto('https://nowsecure.nl'); // Passes Cloudflare & Bot Challengesimport puppeteer from 'puppeteer';
import { createPassport } from 'device-passport';
const passport = createPassport({
seed: 'puppeteer_worker_de_19',
geo: 'DE',
os: 'windows'
});
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
// Apply viewport, headers, userAgent, and client-hints
await passport.injectToPuppeteer(page);
await page.goto('https://bot.sannysoft.com');Tools that drive Chrome over a bare DevTools Protocol socket cannot use the Playwright or
Puppeteer adapters, because those return framework-shaped options. getCdpPlan() returns
an ordered, transport-agnostic command plan instead:
import { createPassport, applyCdpPassport } from 'device-passport';
const passport = createPassport({ seed: 'worker_de_19', geo: 'DE', os: 'windows' });
// `send` is yours: a WebSocket, stdio, or an in-process socket.
await applyCdpPassport(passport, (cmd) => transport.send(cmd.method, cmd.params));The plan covers Page.enable, extra HTTP headers, Emulation.setUserAgentOverride
(with Client-Hints metadata), timezone, locale, device metrics and touch emulation, and
installs the stealth script through Page.addScriptToEvaluateOnNewDocument.
Page.enableis mandatory. Without it Chrome accepts the init script, returns an identifier, and then never runs it — the persona stays a plain automation session.applyCdpPassportemits it first for that reason.
API-level spoofing is not sufficient on its own. canvas.toDataURL() and
gl.readPixels() hand back the real framebuffer, so a headless browser exposes
SwiftShader while its User-Agent claims a discrete GPU — and every persona rendered on
the same machine produces a byte-identical canvas hash, which makes a fleet easy to
cluster.
getStealthScript() now appends a deterministic pixel-protection layer that patches
toDataURL, getImageData, readPixels and the audio analyser:
import { createPassport, getStealthScript } from 'device-passport';
const passport = createPassport('alice');
const script = getStealthScript(passport);
// Use with Playwright addInitScript, Puppeteer evaluateOnNewDocument, or CDP.Properties:
- Deterministic per seed — the same account reads back identical pixels forever, which is what makes "sticky hardware" true beyond the API layer.
- Distinct per account — different seeds produce different noise tables.
- Plausible — a discrete quantization of a few levels, not per-pixel dithering. Real GPUs quantize; a smooth ±1 RGB jitter on every pixel is itself a fingerprint.
- Self-contained — dependency-free IIFE, safe to inject repeatedly (re-injection is a no-op).
Managing 500+ persistent accounts requires deterministic identity governance. PassportManager provides zero-leakage state binding:
import { PassportManager } from 'device-passport';
const manager = new PassportManager({
storage: 'file', // 'file' | 'memory' | 'redis'
storagePath: './data/fleet-passports.json',
defaultGeo: 'US'
});
// Idempotent retrieval: account always returns the same passport
const passport = await manager.getOrCreate('user_account_9041', {
geo: 'GB',
os: 'macOS'
});
// Perform non-breaking lifecycle evolution
// Updates Chrome v124 -> v125 without mutating hardware (cores, screen, RAM remain locked)
await manager.evolveBrowserVersion('user_account_9041');
// Bulk fleet export for distributed clusters
const fleetSnapshot = await manager.exportAll();When the browser is ego-lite, one account maps to three things that must travel together: a sticky passport, an ego-lite profile binding (cookies and login state persist per profile), and a proxy declaration.
import { AccountProfileManager } from 'device-passport/ego';
const manager = new AccountProfileManager({
store: './data/accounts.json', // atomic JSON persistence
// bridge: egoLiteBridge, // optional; inject ego-lite's native bridge
});
// List the profiles ego-lite already has — there is no create-profile API.
const profiles = await manager.listEgoProfiles(); // [{ id, name, isDefault }]
// Bind one account (seed) to one profile and declare its proxy.
await manager.bindProfile('account_9041', {
profileId: 'Profile 2',
proxy: { url: 'http://user-jp:[email protected]:7000', description: 'Tokyo residential exit', country: 'JP' },
});
// The complete launch plan: CDP commands, init scripts, headers, profileId, proxy declaration.
const plan = await manager.createSessionPlan('account_9041');
plan.readiness; // 'ready' | 'missing-proxy' | 'missing-profile'
plan.launchable; // true only when a proxy is declaredego-lite cannot configure a proxy at the browser layer, and neither can this library.
- CDP has no
Browser.setProxySettings— Chromium has never shipped one. --proxy-serveris a browser launch flag on the process, not a CDP command; it cannot be changed after the profile is running.- ego-lite's native bindings expose no proxy setter either.
A proxy is therefore bound outside the browser: at the OS level (system proxy / TUN / transparent redirect), or by routing the ego-lite process through a local forwarder that owns the upstream proxy. This layer refuses to fake it: an account profile is ready only when a proxy is declared, missing-proxy otherwise, and every session plan repeats that state (plan.proxy.bound, plan.proxy.resolution === 'system-level') so an unbound proxy can never be mistaken for an effective one.
Verify any passport or raw external request headers against the theoretical coherence framework:
import { createPassport } from 'device-passport';
const passport = createPassport({ seed: 'eval_demo', geo: 'US' });
// Run the formal audit engine
const evaluation = passport.evaluate();
console.log(evaluation);
/*
{
coherenceScore: 1.0,
status: 'COHERENT',
contradictionCount: 0,
metrics: {
hardwareVsPlatform: { status: 'MATCH', detail: 'macOS arm64 == MacIntel / ARM' },
clientHintsCoherence: { status: 'SYNCHRONIZED', entropyScore: 1.0 },
geoChronoAlignment: { status: 'VALID', timezone: 'America/New_York', locale: 'en-US' }
},
contradictions: []
}
*/device-passport bundles a high-performance zero-dependency CLI for terminal workflows, bash scripts, and CI/CD pipelines.
# 1. Create a deterministic passport for a seed
npx device-passport create --seed crawler_node_01 --geo JP --os macOS
# 2. Output formatted JSON headers for curl / hrequests
npx device-passport headers --seed crawler_node_01 --geo JP > headers.json
curl -H @headers.json https://httpbin.org/headers
# 3. Audit an arbitrary header file for WAF contradictions
npx device-passport audit --file ./headers.json
# 4. Interactive visual persona inspection
npx device-passport inspect --seed production_user_102 ┌────────────────────────────────────────┐
│ User Seed / Account UUID + Geo Target │
└──────────────────┬─────────────────────┘
▼
┌────────────────────────────────────────┐
│ Deterministic PRNG (xoshiro256**) │
└──────────────────┬─────────────────────┘
▼
┌───────────────────────────────┴───────────────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Hardware H │ │ Software S │ │ Geo-Chrono G │
│ Cores / RAM │ │ Chrome / OS │ │ Locale / TZ │
│ GPU / Screen│ │ Full Versions│ │ Accept-Lang │
└───────┬──────┘ └───────┬──────┘ └───────┬──────┘
│ │ │
└───────────────────────┬───────┴───────────────────────────────┘
▼
┌────────────────────────────────────────┐
│ Client-Hints Synthesis Vector C │
│ Sec-CH-UA, Platform, Arch, Bitness │
└──────────────────┬─────────────────────┘
▼
┌────────────────────────────────────────┐
│ Constraint Solver & Coherence Guard │
│ C(P) == 1.0 Zero-Contradiction Gate │
└──────────────────┬─────────────────────┘
▼
┌───────────────────────────────┼───────────────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Native Fetch │ │ Playwright │ │ Puppeteer │
│ Headers │ │Context Options│ │ Emulation Hook│
└──────────────┘ └──────────────┘ └──────────────┘
│ │
└───────────────┬───────────────┘
▼
┌───────────────────────────┐
│ Pixel-Level Protection │
│ canvas / WebGL / audio │
│ deterministic per seed │
└───────────┬───────────────┘
▼
┌───────────────────────────┐
│ Raw CDP Command Plan │
│ (lite-browser, custom) │
└───────────────────────────┘
| Option | Type | Default | Description |
|---|---|---|---|
seed |
string | number |
undefined |
Deterministic persona seed (account name, proxy IP, worker ID). If omitted, generates an ephemeral persona. |
geo |
string |
'US' |
ISO 3166-1 alpha-2 country code ('US', 'JP', 'GB', etc.) or residential proxy URL. Automatically aligns language & timezone. |
os |
'windows' | 'macos' |
undefined |
Preferred operating system. |
browser |
'chrome' | 'edge' | 'safari' |
undefined |
Preferred browser family. |
store |
string | StorageAdapter |
undefined |
File path ('./data/passports.json') or custom storage adapter for automatic persistence. |
fake-useragent only provides a single legacy User-Agent string. Modern anti-bot solutions (Cloudflare, DataDome, Akamai) inspect W3C Client-Hints (Sec-CH-UA, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch), screen metrics, and WebGL renderers. device-passport models the entire 4-tuple device persona with mathematical consistency
Yes! As long as you provide a constant seed (e.g. account username) and specify a store path (or pass the same seed), device-passport guarantees byte-for-byte identical hardware, browser build, and headers forever.
No. device-passport is lightweight (< 1.5MB footprint). It generates headers for pure HTTP requests (fetch, axios, got, curl) as well as optional configuration dictionaries for Playwright/Puppeteer when full browser automation is required.
If you use device-passport in your academic research, empirical benchmarks, or industrial cybersecurity papers, please cite as follows:
@software{device_passport_2026,
author = {webkubor and Open Source Contributors},
title = {device-passport: Stateful Digital Device Passports for Modern Anti-Detection and Web Automation},
year = {2026},
url = {https://github.com/webkubor/device-passport},
version = {1.0.0}
}MIT © webkubor — Open-source and free for both commercial and research usage.
