Feature
Fingerprint and its editor
- 4
- Generator generation
- 5
- Templates
- 29 desktop, 34 mobile
- Self-test checks
- 114
- Countries with a clock and a language
A fingerprint is a whole machine, described field by field
A site does not read one string. It reads the screen, the graphics card, the core count, the memory, the installed fonts, the clock, the language stack, how many microphones and cameras answer, what the connection reports, and how large the page heap may grow. Each answer is ordinary on its own. Read together they name a computer.
So ARMANOS does not patch a user agent. It builds a generated fingerprint of 34 fields, one of which holds 11 client hints of its own, and presents that whole machine to the page. Every profile gets its own set.
Nothing about it is stored. The fingerprint is derived again from the template and the profile id every time the profile starts. That is why the same profile is the same computer on your laptop, on your desktop, and on a teammate's machine after a sync.
- navigator.platform
- MacIntel
- User agent
- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36
- Client hints platform
- macOS 15.1.0
- Architecture, bitness
- arm, 64
- uaFullVersion
- 153.0.7978.0
- Screen, available
- 1470x956, 1470x931
- devicePixelRatio
- 2
- WebGL vendor
- Google Inc. (Apple)
- WebGL renderer
- ANGLE (Apple, ANGLE Metal Renderer: Apple M1 Pro, Unspecified Version)
- Cores, memory
- 8, 8 GB
- Time zone
- Europe/Rome
- Languages
- it-IT, it, en
- Fonts
- 85 names, starting Helvetica Neue, Helvetica, Arial
- maxTouchPoints
- 0
- Mics, outputs, cameras
- 1, 1, 1
- Speech voices
- it only
- Connection
- 10 Mbit/s, 50 ms, 4g
- Heap old generation
- 4096 MB
- Canvas, audio seeds
- 1884544265, 3829808191
- WebRTC policy
- disable-non-proxied-udp
A real draw: template macOS / Chrome, profile id acct-42, generation 4, engine 153.0.7978.0, no proxy attached. Run it again on any machine and you get exactly this.
Identity
platform, user agent, vendor, and the client hints block: brands, full version list, platform version, architecture, bitness, model, mobile flag, form factors.
Screen
width and height, available width and height after the menu bar, colour depth, device pixel ratio.
Hardware
WebGL vendor and renderer, hardware concurrency, device memory, max touch points, the font list.
Place
time zone, the language stack, the primary locale, and the country when both came from the same draw.
Second patch fields
audio inputs and outputs, cameras, speech voice locale, downlink, round trip time, effective connection type, and the old generation heap limit.
Noise seeds
a fixed canvas seed and a fixed audio seed, hashed from the profile id, plus the WebRTC policy.
One seed decides everything, so nothing can contradict itself
Every value is picked by hashing the profile id together with a per-field label. There is no randomness at launch, and there is no second draw that could disagree with the first.
That matters because the strongest tells are not odd values. They are pairs. An Apple M1 renderer next to architecture x86 is a machine that has never shipped, and reading both takes a fingerprinting service one call. So the draws are chained: the graphics card is picked first, and the rest of the hardware follows from it.
Numbers that a real browser rounds are rounded the same way here. Device memory is capped at 8, because Chrome caps it whatever the machine holds. Round trip time is a multiple of 50 ms. Downlink is capped at 10 Mbit/s. The effective connection type is computed from the round trip time using Chromium's own thresholds of 2010, 1420 and 272 ms, not chosen separately.
- 1
Draw the graphics card
One vendor and renderer pair from the template's pool. macOS has 10 pairs, Windows 7, Linux 7, so profiles stop sharing one card.
- 2
Read the cores and memory off it
Each card class carries the counts that card actually ships with. An Apple M4 is 10 cores, an M1 is 8, a Kaby Lake laptop chip never gets 16.
- 3
Read the architecture off it
An Apple Silicon renderer forces arm, everything else x86. Both card families sit in one picker, so this is one click away from impossible.
- 4
Narrow the OS versions
M4 starts at macOS 15.1, M3 at 14.1, M2 at 13.0, and a 2017 Iris Plus card stops at 13.9. A newer chip on an older system is one line to check.
- 5
Draw the screen, then a ratio that screen can have
On Windows the logical size times the ratio is the real panel. 3840x2160 at 1.5 would mean a 5760x4320 monitor, which nobody makes.
- 6
Draw the country, then the clock and the language from it
One throw for both, so the time zone and the language stack always belong to the same place.
- 7
Fix the noise seeds
Canvas and audio seeds are hashed from the profile id once. Two reads of the same canvas agree, and two profiles differ. Fresh randomness per read would itself be the tell.
Five templates, and a phone is taken whole
A template is a class of machine, not a label. It carries the platform string, the user agent pool, the screens, the card pool, the core counts, the menu bar inset, the touch point count and the font sets. Pick the one your accounts would plausibly be used from.
The three desktop templates assemble parts from pools, because people really do build different computers. The two mobile templates do not. A phone's screen, pixel density, graphics card, cores, memory and vendor fonts are fixed by the model, so the profile receives one of 22 real handsets or 7 real tablets whole. A Pixel with a Samsung screen is not a rare device, it is a tell.
The tablet template reports mobile as false and a user agent without the Mobile token, which is what Chrome on a tablet really does. Sites then serve it the desktop layout. A tablet that called itself mobile would contradict itself in the very first header.
| Template | The machine it describes | Pick it when |
|---|---|---|
| macos-chrome | MacIntel, Apple Silicon or Intel graphics, macOS 13.6 through 26.0, pixel ratio 2 | your accounts were opened on a Mac, or you want the tidiest desktop profile |
| windows-chrome | Win32, NVIDIA, Intel or AMD cards, platform version 10.0.0 through 19.0.0, ratio 1 to 1.5 | the population you blend into is mostly Windows, which is most marketplaces |
| linux-chrome | Linux x86_64, Mesa renderers, empty platform version, ratio 1 | you run developer, hosting or ad tooling accounts where Linux reads as normal |
| android-chrome | Linux armv8l, one of 22 real handsets taken whole, 5 touch points, mobile true | the platform treats mobile sessions differently, as social networks do |
| android-tablet | the same, from 7 real tablets, mobile false and no Mobile token in the user agent | you want a tablet, which is served the desktop layout while still reporting touch |
The proxy country draws the clock and the language in one throw
When a profile has a proxy, ARMANOS resolves the exit country and caches it. A directory of 114 countries maps that code to one time zone and one language stack. A German exit gets Europe/Berlin with de-DE, de, en. A Brazilian exit gets America/Sao_Paulo with pt-BR, pt, en.
The country is drawn once and both values are read off it. Two independent draws would eventually produce Asia/Tokyo next to pt-BR, and a mismatched environment sits right behind a shared address in ban post-mortems. One throw makes that combination unreachable.
Large countries get spread. Eight of them carry several zones, so a hundred American proxies do not all read New York. The language stays whole for the country, because an American exit should speak en-US. When there is no proxy at all, a country is drawn by seed from a pool of 50, so the profile still has a real clock instead of yours.
The time zone
Intl, Date and getTimezoneOffset all move together, and the audit compares against the canonical ICU name, since Europe/Kyiv answers as Europe/Kiev.
The language stack
The full list, not just the first entry. Real Chrome carries the base language too, and a single-entry list beside a foreign exit is itself the mismatch.
The speech voices
Voices are an inventory of the system. A profile claiming Brazil should not offer Japanese voices, so the list is limited to the profile's language.
Nothing else
Screen, card, cores, memory and fonts stay as drawn. Moving hardware with the proxy would change the machine an account already knows.
A proxy with no resolvable country
The app says so in a notification and launches anyway. Rewriting the fingerprint later would show the account a new computer.
The editor gives you choices, not a text box
Open a profile's fingerprint editor and you get pickers. Screen, cores, memory, pixel ratio, time zone, languages and graphics card on a desktop template. On a phone or tablet you choose the device model instead, plus the clock and the languages, because those belong to the person and not to the handset.
Every list is derived from the same pools the generator draws from, so you can always find the value your own profile received. Leave a field empty and it stays generated, which is the right default for most profiles.
The editor refuses what a real machine never reports, and it names the field rather than dropping it quietly. It also pulls the dependent values along. Choose a card and the cores, the memory, the architecture and the OS version follow it. Choose a time zone and the language stack follows. Choose a language and the clock follows, because the link has to work in both directions.
A value outside the offered list
Refused by name at save time. A stale picker cannot silently lose a setting you thought you had applied.
Cores or memory the chosen card never has
Integrated laptop graphics with 16 cores is refused. The card and processor pair is the first thing anti-fraud systems compare.
A pixel ratio the chosen screen never has
Checked against real panel sizes, so you cannot save a monitor that is not manufactured.
A template your profile's generation does not know
An older profile is told plainly. Accepting it would show a phone in the card and a Mac to the site.
Anything typed by hand
There is no free text field. Seven CPU cores is a stronger tell than the generated value would ever have been.
Overrides that stop fitting after a template change
Re-checked on the spot and named, instead of being dropped at launch while the editor keeps showing them.
A profile keeps the generation it was born under
Because nothing is stored, any change to the generator or to a pool hands every existing profile a different computer at the next start. That was measured when the pools widened: eight profiles out of eight changed cores, memory, screen, graphics card and font list. A profile warmed for months would arrive at its accounts as a new machine, which is what a takeover looks like.
So each profile carries a generation number. The current one is 4. Profiles made before versioning are served by a frozen copy of the first generator, the second generation has its own frozen file, and the third is held in place by a byte-level snapshot test. Any edit that moves a third generation machine by one byte fails that stand.
The generation travels with the profile, so a synced copy is generated by the same algorithm on the other machine. A profile from a newer build than the one you are running refuses to launch here, and says so, rather than presenting a different computer.
Generation 4 is where the clock and the language became real. Before it, a hundred profiles of one template shared a null time zone and two English language stacks. It also removed screens that could not exist, tied the OS version to the graphics card, and added the device, voice, network and heap fields the engine's second patch reads.
Why your old profiles look unchanged
The self-test asks the browser about itself
The audit opens a page with the profile's exact seed and reads back what a site would read. It runs headless in a throwaway directory, so it never competes for the profile's own folder and never disturbs a session you have open. It loads the same protection extension a real launch loads, because measuring a browser you never actually get would report failures you do not have.
A desktop profile of the current generation runs 29 checks. A phone or tablet runs 34, adding touch points, touch events, coarse pointer, screen orientation and the window fitting inside the declared screen. You get a pass or fail per line with the measured value, a score, and the name and version of the engine that was measured.
Some checks look for the machine, and some look for the substitution itself. A patched function that no longer reads as native code proves tampering more clearly than an honest mismatch would.
The values
Platform, client hints platform version and architecture, screen, cores, memory, WebGL vendor and renderer, time zone, primary language and the full list.
The hiding
navigator.webdriver reads false and its getter reads as native code, getParameter reads as native code, window.chrome is present, plugins are not empty.
The canvas
Four separate lines: two reads agree, the same pixel read through two rectangles agrees, transparent pixels stay transparent, and the noise mask cannot be lifted by subtraction.
Other realms
An iframe and a worker must answer the same cores and platform as the document. On a JavaScript level engine they do not, and that difference is measurable from the page.
Fonts
Measured the way font fingerprinting really works, by rendering a string and comparing widths. Wingdings, Zapfino and Copperplate must be invisible unless the profile has them.
The second patch fields
Device counts, voice languages, the connection numbers within the browser's own rounding, and the heap limit against V8's own formula.
Where the substitution happens decides how deep it goes
On the ARMANOS Browser, the fingerprint lives in C++ inside the engine and is applied before any page script runs. Every realm inherits it, so a worker and an iframe answer the same as the document. The build reports Chrome 153.0.7978.0, and the claimed version is threaded through the user agent and every client hint from the running engine, so the number can never contradict feature detection.
On the built-in engine, the same fingerprint is installed by an injected script at document start, in every frame. It also hides itself: patched functions report native source, keep the original name and argument count, and are defined as methods so they are not constructible. deviceMemory is defined only on a secure origin, because real Chrome has nothing there on plain http.
The injected route defeats the common checks. It cannot match a compiled engine on realm consistency, and the audit shows you that difference instead of hiding it. Choosing the engine is a switch in the app, and the self-test names which one it measured.

What this does not do
- It does not promise that accounts stay unbanned or that a profile is undetectable. A machine that agrees with itself removes one class of signal. Behaviour, address reputation and account history are separate problems.
- The clock does not follow the exact exit city. The country decides it, and within a large country the zone is drawn by seed. A proxy in Los Angeles can get any of the four American zones. Changing that would move existing profiles onto a different machine, so it is deliberately left for a later generation.
- Media devices are only trimmed, never invented. A profile shows a subset of the real machine's devices. A computer without a camera still reports none, because a fake device identifier would be a tell of its own.
- On the shipped build, WebGL readPixels still returns bytes that bypass the canvas noise. The self-test reports that line red on purpose rather than showing a green it has not earned. The fix is a pending engine patch and needs a rebuild.
- You cannot type a fingerprint in or import one from a file. Every editable value is a choice from a list, and everything else is derived from the seed.
How to check
Each claim above is implemented in one place and guarded by a stand you can run.
- Every value comes from one seed, and the parts agree with each other
- packages/shared/src/index.js · apps/desktop/test/fingerprint-consistency.js
- A profile keeps its generation, and generation 3 is frozen byte for byte
- packages/shared/src/fingerprint-legacy.js · apps/desktop/test/fingerprint-version.js · packages/shared/test/поколение-3-заморожено.js
- The editor offers exactly what the generator can produce, and refuses the rest
- packages/shared/src/index.js · packages/shared/test/выбор-редактора-полон.js
- The self-test's rules run in a real browser and the whole audit executes
- apps/desktop/src/lib/fpAudit.js · apps/desktop/test/fp-audit.js · apps/desktop/test/audit-on-builtin.js
- An iframe and a worker answer the same machine as the document
- apps/desktop/src/injected/fingerprint.js · apps/desktop/test/fingerprint-realms.js
Read next
Profiles
What else a profile carries besides its machine: cookies, storage, extensions and a start page.
Proxies
How the exit country is resolved and cached, and why it decides the clock.
The engine
The patched Chromium build, what it substitutes in C++ and what is still pending.
Fingerprint tool
See what your current browser reveals, in the same fields the profile describes.
See what your own browser reveals
Read your current fingerprint field by field, then compare it with what a profile presents.