ARMANOS

Scenario

Privacy

A profile for ordinary browsing. It blocks a list of tracker domains, strips tracking parameters from every navigation, forbids WebRTC to go around your proxy, and keeps cookies apart from your other profiles. It does not make you anonymous, and this page says exactly where the line runs.
31 domains
Blocklist for this scenario
40 parameters
Removed on every navigation
disable_non_proxied_udp
WebRTC policy
ARMANOS Browser 153.0.7978.0
Engine
3
Free tools that send nothing

Tracking is not what one site knows about you, it is what many sites share

Open a page and it loads its own files first. Then it loads files from companies you have never heard of: an analytics script, an ad exchange, a session recorder. Each of those requests carries your address, your browser's answers about itself, and often a cookie set on a domain you never visited.

That is the whole mechanism. Two unrelated sites load the same third-party file, so one company sees both visits and joins them under a single identifier. Neither site had to cooperate with the other.

The link you clicked does the same job without a cookie. A click id in the query string travels with you and lands in the destination's own database. Then there is the device itself: screen, graphics card, fonts, clock, audio. Those answers are stable, and taken together they are rare.

The tools people usually reach for each close one road. This scenario is about closing four of them inside one profile, and about naming the roads that stay open.

ApproachClosesLeaves open
Incognito modeCookies, when the window closesThe same device answers, the same address, the same click ids
A VPNThe address a site seesCookies, fingerprint, WebRTC candidates, link parameters
A fingerprint-spoofing extensionSome values, rewritten inside the pageRewritten properties a page can inspect; the network layer untouched
A second browser profileThe cookie jarOne machine underneath: same fonts, same GPU, same local address
A virtual machineAlmost everything, honestlyGigabytes and a separate update cycle per identity
This scenarioCookies, device, tracker requests, link parameters, WebRTC candidatesYour provider, and any site you sign into

The scenario is a record in the code, and it is read at every launch

A scenario is not a mood. It is an object in packages/shared/src/index.js that names the start page, the injected scripts, the tracker lists to load, whether links are cleaned, and the daily action ceilings.

Privacy is also the fallback. When a profile arrives with a scenario the code does not know, profiles.js writes privacy instead of failing, and the spreadsheet importer uses it as its own default. So a profile created in a hurry lands here rather than nowhere.

One field is empty on purpose. The other scenarios carry daily ceilings on platform actions, because posting or messaging at machine speed is what gets accounts noticed. Privacy carries none, and its entry in SCENARIO_DAILY_LIMIT is zero. You are reading, not acting on a platform.

Change the scenario later and it takes effect on the next launch. The request hook remembers which scenario id it was built for and is rebuilt when that changed, so the previous scenario's rules are dropped rather than left quietly running.

id
privacy
Start page
https://duckduckgo.com
Injected scripts
fingerprint, stealth
Tracker lists
general
Link cleaning
on
Daily action ceiling
none
Status
shipped (feasibility: now)

Read from SCENARIOS.privacy and SCENARIO_DAILY_LIMIT in packages/shared/src/index.js.

The blocklist is thirty-one domains, and both engines read the same list

The general list holds 31 hosts: web analytics, ad exchanges, product analytics, session recorders, crash and performance reporting, mobile attribution. An entry matches the host and its subdomains. The matcher also accepts a host plus a path fragment, though every entry in the general list is a bare host.

The two engines then take different roads to the same rules. On ARMANOS Browser the list is compiled into declarativeNetRequest rules inside a small extension written per profile at launch, so blocking happens in the network stack with no hook a page can see. The built-in fallback engine has no such stack, so the app cancels the request itself in session.webRequest.

The resource types are spelled out, main_frame included. Leave them out and declarativeNetRequest matches everything except main_frame, so a click that navigates straight onto a blocked domain would pass on one engine and be stopped on the other. Two engines disagreeing is worse than either answer alone.

What a domain list cannot do is first-party analytics. When a site measures you from its own address, there is no third-party host to match. ARMANOS does have path rules for that, but only for the platforms they were tested against, and the Privacy scenario is given none. Inheriting another scenario's path rules would break unrelated pages for no gain.

CategoryExamples from the list
Web analyticsgoogle-analytics.com, googletagmanager.com, analytics.google.com
Ad exchangesdoubleclick.net, googlesyndication.com
Social pixelsgraph.facebook.com, pixel.facebook.com
Session recordinghotjar.com, fullstory.com, logrocket.com, mouseflow.com
Product analyticsmixpanel.com, amplitude.com, segment.com, heap.io
Crash and performancesentry.io, crashlytics.com, bugsnag.com, newrelic.com
Mobile attributionappsflyer.com, adjust.com, branch.io, kochava.com
The Protection screen in ARMANOS with counters for blocked requests today, over seven days and in total, broken down by type and by profile.
Every blocked request and every cleaned link is counted per profile and kept when you close the app. This shot is a fresh install, so the counters still read zero.

Forty tracking parameters come off before the request leaves

TRACKING_PARAMS is a flat list of 40 query parameter names: click ids from ad platforms, campaign tags, mail campaign ids, analytics linkers, and the professional-network set the cleaner started from. It is a named list, not a guess about what looks like tracking.

On ARMANOS Browser it becomes one declarativeNetRequest rule with a queryTransform that removes those names from main_frame and sub_frame requests. The address bar shows the clean URL and the parameter never reaches the page at all. On the built-in engine the main-frame request is redirected to the cleaned URL before it goes out.

The cleaning rule deliberately has lower priority than the blocking rules. A URL that is both a known tracker and carries a click id must be blocked, not cleaned and then let through. Getting that order wrong would turn a block into a polite pass.

Each cleaned navigation increments a counter you can read on the Protection screen. Cleaning touches only the query string: an identifier placed inside the path, or after the #, is left where it is.

  • Ad platform click ids

    fbclid, gclid, msclkid, dclid, ttclid, twclid, yclid, wbraid, gbraid, rdt_cid, ScCid, epik, igshid, mibextid, li_fat_id

    15
  • Campaign tags

    utm_source, utm_medium, utm_campaign, utm_content, utm_term

    5
  • Mail campaign ids

    mc_cid, mc_eid

    2
  • Analytics linkers

    _ga, _gl

    2
  • The professional-network set the cleaner started from

    trk, trkInfo, lipi, lici, src, trackingId, refId, eBP, recommendedFlavor, midToken, midSig, trkEmail, connectionOf, orig, anchorTopic, contextUrn

    16

WebRTC is not allowed to go around the proxy

WebRTC exists so two browsers can talk directly. To arrange that it gathers candidate addresses, including the address of your machine on your own network, and hands them to the page that asked. No permission prompt is involved and nothing appears on screen.

For a set of profiles that is the worst leak there is. The local address is identical in every profile on the computer. Ten profiles on ten different proxies, one and the same 192.168 address, and the whole group is tied together by a value no proxy can change.

ARMANOS sets the same policy on both engines. ARMANOS Browser launches with --force-webrtc-ip-handling-policy=disable_non_proxied_udp. Built-in profile windows get setWebRTCIPHandlingPolicy with the same value, and the policy is also carried in the profile's fingerprint as webrtcPolicy.

The app measures this rather than claiming it. The network diagnostics row used to be a sentence about the setting, and it would have kept printing calmly after the setting stopped applying. Now a hidden window opens with the same policy, candidates are gathered and classified: a raw private address is a failure with the address shown, a changing .local name is a pass, and a measurement that did not finish reports itself as unknown rather than as clean.

  1. 1

    Run the leak test in your everyday browser

    The tool builds an RTCPeerConnection with an empty ICE server list, so nothing goes out, and lists the addresses your browser hands over unasked.

  2. 2

    Run it inside an ARMANOS profile

    Launch a profile that has a proxy and open the same page in it.

  3. 3

    Read the difference

    A changing name ending in .local is the browser hiding the address on purpose. A raw 192.168 or 10.x address is the value that ties your profiles together.

The browser is built without the parts that phone home

ARMANOS Browser is our own Chromium build, and the build arguments strip the reporting out. enable_reporting is false, enable_rlz is false, the Google API keys are empty, and extended Safe Browsing reporting is off. Safe Browsing itself stays compiled because setting its mode to zero breaks the build; with no API keys it has no lookup server to reach.

In the page, the stealth script replaces navigator.sendBeacon. Beacons fire while a page is unloading, which is exactly when a tracker ships what it collected, and an ordinary request filter no longer sees them in time. The replacement returns true, so pages behave normally, and it adds one local counter.

The profile start page is served by a loopback HTTP server inside the app, so a launch is never announced to anyone. The single outbound request it makes is the IP lookup, and that goes through the profile's own proxy, because the point of the page is showing you the exit address a site would see.

Cookies and site logins stay in the profile directory on your computer. Only profile settings reach your account. When a session moves between your own machines it travels through a folder you choose, as a file encrypted with scrypt and AES-256-GCM.

enable_reporting
false
enable_rlz
false
use_official_google_api_keys
false
google_api_key
empty
enable_extended_safe_browsing_reporting
false
safe_browsing_mode
1, with no keys to reach a server

The gn arguments the shipped engine is built with, from engine/scripts/build-mac.sh. The Linux build reuses the same list.

Check the result yourself, and we never see the numbers

Three free tools live on this site, and all three compute inside your browser. That is not a promise about our servers, it is the shape of the code: there is no fetch, no XHR, no beacon and no socket in any of them, and a stand fails the build if one appears.

The fingerprint check reads twelve values, including the ones that decide the matter and that many checkers skip: the pixels a WebGL readback returns, the audio rendering, the font list. It condenses them into an eight-character code you can compare by eye.

The leak test asks the browser for its own candidate addresses with an empty ICE server list. Pointing it at somebody's STUN server would tell that server about you, which is the opposite of the job it exists to do.

The third tool counts distinct machines. Profiles are separate browsers with no shared memory, so a page in one knows nothing about another, and we cannot count for you even if we wanted to. You paste the codes, it counts the repeats.

  1. 1

    Measure your everyday browser

    Open the fingerprint check and copy the eight-character code.

  2. 2

    Measure each profile

    Open the same page inside each ARMANOS profile and copy each code.

  3. 3

    Count the repeats

    Paste all the codes into the counter on that page. Two profiles with one code look like one machine to a site.

  4. 4

    Go deeper inside the app

    The profile fingerprint window runs a self-test of 25 readings against the browser that profile actually launches with, and scores it out of 100.

What this does not do

  • It does not make you anonymous. Your provider still sees that you connected, and any site you sign into knows exactly who you are.
  • The blocklist for this scenario is 31 domains. It is a curated list of well-known trackers, not a full ad-blocking ruleset, and a site's own first-party analytics is not on it.
  • Link cleaning removes 40 named query parameters. An identifier placed in the path, or after the #, survives.
  • Without a proxy the profile goes out from your own address. Isolation and a consistent device are not an IP change.
  • On the built-in fallback engine an extension's own blocking rules are executed by ARMANOS, not by the engine. Rules it cannot express are counted and skipped, and that number is printed rather than hidden.
  • We do not claim that sites cannot tell. What is shipped here is isolation, a consistent device, and less data leaving on its own.

How to check

Every claim above has a file behind it and, where it can be measured, a stand that fails the moment it stops being true.

The scenario record says what this page says
packages/shared/src/index.js (SCENARIOS.privacy, SCENARIO_DAILY_LIMIT) · apps/desktop/src/lib/profiles.js (create)
Tracker blocking and link cleaning really run on the shipped engine, measured through our own loopback proxy
apps/desktop/src/lib/tracker-rules.js · apps/desktop/test/блокировка-живьём.js
This scenario inherits no path rules written for other platforms, and both engines compile the same rules with their hosts
apps/desktop/src/lib/forkExtension.js · apps/desktop/test/слежка-по-хостам.js
An enabled blocker never promises more than the engine delivers, and unparsed rules are counted
apps/desktop/src/lib/dnrBlock.js · apps/desktop/test/builtin-blocking.js · apps/desktop/test/extension-limits-honest.js
The WebRTC verdict is measured, not asserted
apps/desktop/src/main/main.js (probeWebrtc, classifyWebrtcCandidates) · apps/desktop/test/leak-verdict.js
The free tools make no network call at all
apps/web/components/tools/LeakCheck.tsx · apps/web/test/тест-утечки.js · apps/web/test/инструмент-отпечатка.js

Two profiles on the free plan

Install ARMANOS, create a profile on the Privacy scenario, then run the fingerprint check in it and in your everyday browser and compare the two codes.