Skip to content

botonomus.fingerprint

Seeded personas and the Botonomus Chromium switches that apply them.

botonomus.fingerprint

Seeded personas and the Botonomus Chromium switches that apply them.

A seed (by default derived from the profile name and a per-installation key) becomes a Persona whose values are realistic for Windows desktops and never exceed the host's hardware; Persona.to_switches renders it as --bn-* command-line switches for Botonomus Chromium.

GPU_TABLE module-attribute

GPU_TABLE: Final[Mapping[GpuFamily, tuple[GpuOverride, ...]]] = MappingProxyType({'nvidia': (_angle('NVIDIA', 'NVIDIA GeForce GTX 1060 6GB', 7171), _angle('NVIDIA', 'NVIDIA GeForce GTX 1650', 8066), _angle('NVIDIA', 'NVIDIA GeForce GTX 1660 SUPER', 8644), _angle('NVIDIA', 'NVIDIA GeForce RTX 2060', 7944), _angle('NVIDIA', 'NVIDIA GeForce RTX 3050', 9479), _angle('NVIDIA', 'NVIDIA GeForce RTX 3060', 9476), _angle('NVIDIA', 'NVIDIA GeForce RTX 3070', 9348), _angle('NVIDIA', 'NVIDIA GeForce RTX 4060', 10370), _angle('NVIDIA', 'NVIDIA GeForce RTX 4070', 10118)), 'amd': (_angle('AMD', 'Radeon RX 580 Series', 26591), _angle('AMD', 'AMD Radeon RX 6600', 29695), _angle('AMD', 'AMD Radeon RX 6700 XT', 29663), _angle('AMD', 'AMD Radeon RX 7600', 29824), _angle('AMD', 'AMD Radeon(TM) Graphics', 5688)), 'intel': (_angle('Intel', 'Intel(R) UHD Graphics 620', 22807), _angle('Intel', 'Intel(R) UHD Graphics 630', 16018), _angle('Intel', 'Intel(R) UHD Graphics 770', 18048), _angle('Intel', 'Intel(R) Iris(R) Xe Graphics', 39497))})

Real Windows Chrome WebGL strings (ANGLE on Direct3D 11) grouped by vendor family.

The format matches what stock Chrome reports on Windows; device IDs are the PCI IDs of the listed parts. Every value is a plausible desktop or laptop GPU, but only values from the host's own family are safe to claim.

DEVICE_MEMORY_WEIGHTS module-attribute

DEVICE_MEMORY_WEIGHTS: Final[tuple[tuple[int, int], ...]] = ((4, 6), (8, 30), (16, 44), (32, 20))

(navigator.deviceMemory GB, relative weight) for Windows desktop Chrome.

An estimate shaped by the Steam Hardware & Software Survey's RAM shares (16 GB the plurality, 32 GB second and growing, 8 GB declining, 4 GB rare), expressed as the power-of-two values Chromium reports.

HARDWARE_CONCURRENCY_WEIGHTS module-attribute

HARDWARE_CONCURRENCY_WEIGHTS: Final[tuple[tuple[int, int], ...]] = ((4, 12), (6, 8), (8, 20), (12, 18), (16, 22), (20, 8), (24, 12))

(logical processors, relative weight) for Windows desktop Chrome.

An estimate, not a measurement: shaped by the Steam Hardware & Software Survey's physical-core shares (6 and 8 cores most common) mapped to logical counts with SMT / Hyper-Threading, plus Intel 12th-14th gen hybrid parts at 16, 20 and 24 threads. Replace with BrowserForge / apify fingerprint-suite data (Apache-2.0) when a measured distribution is imported.

GpuFamily

GpuFamily = Literal['nvidia', 'amd', 'intel']

GPU vendor family.

PersonaSpec

PersonaSpec = Literal['auto', 'off'] | int | Persona

"auto" (seed from the profile), "off", an explicit seed, or a persona.

GpuOverride dataclass

GpuOverride(vendor: str, renderer: str)

Unmasked WebGL vendor and renderer strings to report.

Rules (enforced on construction): each string is 1-256 printable ASCII characters (0x20-0x7E) with no leading or trailing whitespace and no ", \ or =.

Attributes:

Name Type Description
vendor str

UNMASKED_VENDOR_WEBGL, for example "Google Inc. (NVIDIA)".

renderer str

UNMASKED_RENDERER_WEBGL, for example "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 (0x00002504) Direct3D11 vs_5_0 ps_5_0, D3D11)".

Raises:

Type Description
ConfigurationError

If either string breaks the rules above.

HostInfo dataclass

HostInfo(logical_cpus: int, memory_gb: float, platform: str, screen: tuple[int, int] | None = None, scale: float = 1.0, windows_server: bool = False)

Hardware of the host that will run the browser.

A persona never claims more than the host can back up: detectors measure parallel speed-up and proof-of-work timing, so extra cores or memory show.

Attributes:

Name Type Description
logical_cpus int

Logical processor count (what stock Chrome reports as navigator.hardwareConcurrency).

memory_gb float

Total physical memory in GiB.

platform str

sys.platform of the host, for example "win32".

screen tuple[int, int] | None

Primary display resolution in physical pixels, or None when unknown (a persona then keeps the real screen).

scale float

Display scale factor (devicePixelRatio at 100% zoom).

windows_server bool

Whether the host runs Windows Server, whose UA-CH platformVersion differs from consumer Windows.

Raises:

Type Description
ConfigurationError

If logical_cpus is not a positive integer, memory_gb or scale is not a positive finite number, or screen is not two positive integers.

device_memory property

device_memory: int

The navigator.deviceMemory value stock Chrome reports on this host.

Mirrors Chromium's ApproximatedDeviceMemory: physical memory in MB is rounded to the nearest power of two (ties round down), converted to GB and clamped to the desktop range 2-32. A "16 GB" machine with 15.8 GiB usable therefore reports 16, not 8.

detect classmethod

detect() -> HostInfo

Measure the current machine.

Blocking, but it only reads cached OS counters and returns in microseconds, so it is safe to call from an event loop.

Returns:

Type Description
HostInfo

The detected host information.

Persona dataclass

Persona(seed: int, hardware_concurrency: int, device_memory: int, timezone: str | None = None, gpu: GpuOverride | None = None, noise: bool = True, screen: tuple[int, int] | None = None, taskbar: int = 48, platform_version: str | None = None)

The hardware and locale identity a Botonomus Chromium profile presents.

Build one with from_seed (or resolve_persona) so its values are realistic and within the host's capabilities; direct construction is for explicit overrides and is validated but not capped.

Attributes:

Name Type Description
seed int

64-bit key for canvas, WebGL and audio noise. C++ derives per-site keys from it and the top-level eTLD+1, so one profile is stable per site and unlinkable across profiles.

hardware_concurrency int

navigator.hardwareConcurrency (1-1024).

device_memory int

navigator.deviceMemory in GB, one of 2, 4, 8, 16, 32.

timezone str | None

IANA time zone, or None to keep the host's zone.

gpu GpuOverride | None

WebGL vendor/renderer override, or None to keep the real GPU.

noise bool

Whether readback noise is on; False is for measurement only.

screen tuple[int, int] | None

Screen size in CSS pixels, or None to keep the real screen.

taskbar int

Taskbar height in CSS pixels (availHeight = height - taskbar); used only with screen.

platform_version str | None

UA-CH platformVersion override (MAJOR.MINOR.PATCH), or None for the host's.

Raises:

Type Description
ConfigurationError

If any field is out of range or malformed. The time zone is checked against the IANA database when one is installed (tzdata); otherwise only its syntax is checked.

from_seed classmethod

from_seed(seed: int, host: HostInfo, *, timezone: str | None = None, gpu: GpuOverride | None = None, noise: bool = False) -> Persona

Derive a realistic persona deterministically from a seed.

Each value is drawn from its weighted distribution after removing candidates above the host's capability, using BLAKE2b of the seed as the random source so the result is identical on every Python version and platform. A host below the smallest candidate reports its own value.

Parameters:

Name Type Description Default
seed int

64-bit seed, in [0, 2**64).

required
host HostInfo

Host capabilities that cap the claimed hardware.

required
timezone str | None

IANA zone to present, or None for the host zone.

None
gpu GpuOverride | None

Explicit GPU override; see the GPU table in botonomus.fingerprint.gpus.

None
noise bool

Canvas, WebGL and audio readback noise. Off by default: measured on 2026-10-05, fingerprint.com's tampering model scored noisy personas 0.76-0.92 (flagged above about 0.8) and noiseless ones 0.24, against 0.48 for no persona. Without noise, personas on one machine share their canvas, WebGL and audio hashes.

False

Returns:

Type Description
Persona

The persona.

Raises:

Type Description
ConfigurationError

For an invalid seed, host, time zone or GPU.

to_switches

to_switches() -> tuple[str, ...]

Render the Botonomus Chromium command-line switches for this persona.

Returns:

Type Description
str

Sorted --bn-*=value switches. GPU switches appear only with a GPU

...

override, --bn-timezone only with a time zone and --bn-noise=0

tuple[str, ...]

only when noise is disabled.

gpu_family

gpu_family(vendor: str) -> GpuFamily | None

Classify a vendor or renderer string into a GPU family.

Parameters:

Name Type Description Default
vendor str

Any vendor-identifying string, for example "NVIDIA", "Google Inc. (AMD)" or a full ANGLE renderer string.

required

Returns:

Type Description
GpuFamily | None

The family, or None when the string names no known family (including

GpuFamily | None

software renderers such as SwiftShader or Microsoft Basic Render Driver).

same_family_gpus

same_family_gpus(host_vendor: str) -> tuple[GpuOverride, ...]

Overrides that stay within the host GPU's vendor family.

Parameters:

Name Type Description Default
host_vendor str

The host's real vendor or renderer string (see gpu_family for accepted forms).

required

Returns:

Type Description
GpuOverride

The curated overrides for that family; empty when the family is unknown,

...

because no override can be backed by the rendered output.

resolve_persona

resolve_persona(spec: PersonaSpec, profile_root: Path, profile: str, host: HostInfo, *, timezone: str | None = None) -> Persona | None

Turn a configuration value into the persona for one session.

Parameters:

Name Type Description Default
spec PersonaSpec

"auto" derives the seed from the profile (see persona_seed); "off" disables the persona; an int is used as the seed; a Persona is used as is.

required
profile_root Path

Profile root directory holding the persona key.

required
profile str

Profile name.

required
host HostInfo

Host capabilities.

required
timezone str | None

Time zone to present (for example from a geo lookup). It fills in an explicit persona's missing time zone but never replaces one.

None

Returns:

Type Description
Persona | None

The persona, or None for "off".

Raises:

Type Description
ConfigurationError

For an unknown spec, invalid seed, profile or time zone, or an explicit persona that claims more cores or memory than the host has.

persona_seed

persona_seed(profile_root: Path, profile: str) -> int

Return the stable 64-bit persona seed of a profile.

The seed is a keyed BLAKE2b of the normalized (lowercase) profile name, keyed with 32 random bytes stored in profile_root/.botonomus/persona.key. The same profile therefore keeps one identity across restarts, while the same profile name on another installation gets an unrelated identity.

The key is created on first use and is never logged. Deleting it changes the identity of every profile under profile_root. Blocking file I/O; it is a single small read after the first call.

Parameters:

Name Type Description Default
profile_root Path

Profile root directory (the one passed to the manager).

required
profile str

Profile name, validated like ProfileLease.

required

Returns:

Type Description
int

The seed, in [0, 2**64).

Raises:

Type Description
ConfigurationError

If the profile name is invalid, or the key file exists but is not exactly 32 bytes.

OSError

If the key cannot be read or created.