ScalebrowserDOCS

Profiles & personas

What a profile is made of: the fields you set, the identity drawn from its seed, which values are taken from your machine rather than invented, and what may be changed after creation.

A profile is one persistent identity: its own browser directory, its own fingerprint, optionally its own proxy. This page is what it is made of and what you may change.

Creating one

Only name is required. Everything else is either drawn from a seed or takes the daemon's default.

bash
$ curl -X POST http://127.0.0.1:8787/v1/profiles \
    -H "Authorization: Bearer $SCALEBROWSER_BEARER_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name": "shop-01", "geo_mode": "follow_exit", "proxy_id": "px_7f3a"}'
FieldSet at createChanged laterWhat it does
namerequiredyesYours. The only field with no effect on anything the page sees.
proxy_idoptionalyesWhich stored proxy this profile leaves through. null means your own address.
geo_modeoptionalyesWhat happens when the persona and the exit disagree. Default follow_exit.
expected_countryoptionalyesThe country strict_expected measures against.
persona_countryoptionalnoThe region the identity is drawn for. See What you may constrain.
group_idoptionalyesA flat label. See Groups, presets & bulk.
enableddefaults trueyesA disabled profile is skipped by leases and bulk starts.
seedoptionalnoReproduce a known identity down to the byte.
engine_versionoptionalnoPin a specific installed engine.

The persona

Every profile carries a full identity, and the whole thing rides in the profile object under persona. It is dozens of fields: user agent, platform, screen, GPU, timezone, locale, fonts, audio properties, speech voices, client hints, media devices, permission defaults, network information, battery, and a platform authenticator.

The interesting question is not what is in there. It is where each value came from, because that is what decides whether a page can catch it.

Values taken from your machine

Anything a page can cross-check against the machine's own rendering is read off the host, not invented. There is no version of this that works by claiming.

AxisWhy it cannot be invented
GPU vendor and rendererThe machine's own drawing answers back. A claimed card that does not draw the way that card draws is a contradiction found in one comparison.
Screen geometryThe desktop answers, in both directions: window size, available area, the taskbar's bite.
Device pixel ratioSame. The display scale is measurable from inside the page.

These are adopted at create and again at every launch, which is what makes a profile survive being moved to another machine of the same operating system: it heals rather than lying about the machine it is now on. Across operating systems it does not heal, and the launch is refused instead: see Tier.

The consequence is honest and worth stating: every profile on one host shares that host's screen and GPU. Within one machine those axes are linkable by design. The alternative is a claim the page can disprove, and a disprovable claim is worse than a shared truth.

Values drawn from the seed

Axes with no hardware feedback are sampled deterministically from the profile's seed: timezone, locale, font set, browser version, device memory, hardware concurrency, audio properties, voices, media devices, battery, network information.

Same seed, same values, every time. That is why a profile is the same machine at every start, and why a seed you kept lets you rebuild a profile you lost.

Values rewritten at launch

Under the default follow_exit, timezone and locale are rewritten to match the proxy exit's cohort. That happens at launch, so moving a profile to a proxy in another country does the right thing without you editing anything.

An exit the daemon cannot identify is left alone rather than half-rewritten. Half a rewrite is a contradiction the profile did not have before. Coherence & proxies is the page for that.

With no proxy the same thing happens against your own connection. The daemon resolves the address the browser really leaves through and rewrites timezone and locale to that country. It is the same rule, not an exception to it: a profile claiming Berlin from an address that geolocates to Vienna is a contradiction any site can check for free, and the timezone is the half we can fix.

This now happens when you create the profile, not only when you first start it. Before, a profile made without a proxy showed a random country in its identity panel until its first launch, and then changed under you. The lookup is capped at four seconds and simply skipped when it fails, so creating a profile offline still works and leaves the drawn identity alone.

What you may constrain

Almost nothing, and that is deliberate.

A caller may narrow an axis the launch does not overwrite, and only as a weight on an existing draw. Of the axes anyone asked for, exactly one qualifies: country. Set it as persona_country when you create one profile, or as a preset's constraints.country for a batch. Both go through the same check.

Choosing a region pins language, timezone and speech voices together, because all three come out of one row of the model. It does not pin anything else: two profiles created for the same region still differ on every other axis.

bash
$ curl http://127.0.0.1:8787/v1/persona/constraints \
    -H "Authorization: Bearer $SCALEBROWSER_BEARER_TOKEN"

That endpoint answers with the country codes the embedded model actually carries locale rows for. Read it rather than writing your own list: the set is a property of a pinned dataset, and a hand-copied list would drift into offering a region the daemon then refuses with a 400 nobody can explain.

A region is a starting value, not a promise. Under the default geo_mode the profile takes the country it really exits in: its proxy's, or with no proxy your own connection's. So the region decides the identity of a profile you run through a matching proxy, or one you deliberately set geo_mode: "off" on. Choosing a region that the exit would overwrite is refused when you create it, with a message naming both countries, rather than accepted and quietly changed.

There is deliberately no prefill of your own country. It would be wrong twice over: the identity model draws only eleven countries while the exit rule can rewrite to seventeen, so a country like Austria is one you can be given but never pick; and the usual case is not your own country but your proxy's, which a prefill would make you clear every time. A profile with no proxy already ends up with your country without anyone choosing it.

Why the list is so short. An extra knob is an extra way for a fleet to look alike. Every constraint a caller can set is one more thing a hundred profiles created by the same script will share, and a shared unusual value is exactly what a fleet is caught by. An unconstrained draw stays byte-identical whether or not you passed a constraint.

The protection verdict

A profile that is listed or fetched carries a protection object beside its own fields:

json
{
  "id": "pr_9c21e4",
  "name": "shop-01",
  "runtime_state": "stopped",
  "protection": { "level": "…", "blocked": false, "reasons": [] }
}

It is produced by the same functions the launch itself runs, so blocked: true means "this profile will refuse to start", not a second opinion that might disagree. The management app deliberately does not render it; the API carries it because a script that starts fifty profiles wants to know which two will fail before it tries.

Deleting one

DELETE /v1/profiles/:id removes the row and the profile's browser directory. A running browser is stopped first.

That takes every cookie, every stored session and every login inside it, and it is not recoverable.

If you sync, deleting here also deletes the synced copy. See Profile sync.

Next