Skip to main content
A profile is the JSON document of values a machine shows to web pages. A seed makes the browser compose one from measured tables. You can also write one yourself and launch it, and then the browser composes nothing. Write a profile when a seed cannot give you the machine you need: to replay a machine you captured, to hold one surface at a fixed value while you test a site, or to present a combination the catalogue does not offer. For everything else, use a seed. A composed machine is built from measured values, and a profile you write is only as coherent as you make it.

The schema

The schema is config/profile.schema.json, schema version 3. Every section is optional. Every object in it is closed, so an unknown field is an error. Profile schema documents every field.

A minimal profile

Save this as profiles/laptop.json:
Launch it with the profile option. It takes a file path, a dict in Python or an object in Node:
The profile sets cores, memory, screen, languages and timezone. It has no platform section, so navigator.platform is the host’s. This output is from a Mac.

Missing sections are the host’s

The browser composes nothing for a profile you write. A section you leave out keeps the host’s value, not a persona’s. That includes the host’s User-Agent, GPU, fonts, voices and every other surface. The profile above, launched on a Linux server, presents that server’s own User-Agent and GPU. To present a whole machine, fill every section. The next section starts from a composed machine. The application field of locale names one tag, and navigator.languages is then Chrome’s default list for it, as with --fingerprint-locale (Locale and timezone). accept_languages sets the list exactly.

Start from a composed machine

From a checkout of the repository, scripts/profile_resolver.py writes the whole profile a seed composes. It is a Python copy of the browser’s compositor, checked against it with golden digests.
The file validates against the schema and launches as the seed’s machine. Edit the values you need and launch the file with profile. Run the script on the host you will launch on. It caps cores and memory at that host’s and records its CPU architecture, as a launch does. The file has no locale section, so GeoIP or the locale and timezone options fill it.

GeoIP and a profile

GeoIP runs for a profile as for a seed. Its answer, and the locale and timezone options, replace the profile’s own locale and timezone. Pass geoip=False in Python or geoip: false in Node to keep the values in the file, as the example above does.

Seeds and switches do not apply

A profile you write replaces composition, so the seed and the per-field switches have nothing to act on:
  • --fingerprint in args together with a profile is refused:
    Python raises ProfileError, and Node raises ProfileResolutionError with the code APOSTATE_ENVELOPE_SEED_CONFLICT.
  • The fingerprint option is ignored with a profile.
  • --fingerprint-platform and the per-field switches such as --fingerprint-hardware-concurrency have no effect.
--fingerprint-explain does not report a profile you wrote. Read the values from a page, as above.

Run it without the packages

The browser takes the profile as base64-encoded JSON in --apostate-profile:
A value that is not base64 stops the launch:
Raw binary covers running the browser yourself.

Validate a profile

From a checkout of the repository, the release contract checker validates a file against the schema:
A profile with a wrong type and an unknown field:
The packages validate a profile before every launch and export the same check:
Node prints ProfileResolutionError profile.cpu.logical_cores must be integer. for the same profile. The schema checks types and fields, not coherence. Nothing checks that your GPU, screen and User-Agent belong to one machine.