# isTrusted false and scroll detection: our robot's tell

> Our warm-up robot scrolled with window.scrollBy, so pages saw isTrusted false and a scroll with no wheel. How a page detects that, and what we changed.

Source: https://mosaicbrowser.com/blog/warm-up-bot-signature

## The warm-up robot that left a bot signature

Our warm-up robot scrolled with window.scrollBy, so pages saw isTrusted false and a scroll with no wheel. How a page detects that, and what we changed.

![false: isTrusted: false, on every scroll it made, beside a screenshot: the Mosaic warm-up robot about to give a new profile a browsing history](https://mosaicbrowser.com/covers/warm-up-bot-signature.webp)

A page can tell a script's scroll from a person's in two lines of JavaScript. A scroll made with `window.scrollBy` fires `scroll` events with no `wheel` event in front of them, and a click made with `element.click()` arrives with `isTrusted` set to `false` and no pointer events around it. Our warm-up robot did both, on every site it visited, for as long as it existed in that form.

## What the robot was for

Warm-up browses a profile before it meets the site that matters. A profile with an empty cookie jar and no history reads as brand new, and "no prior relationship with the web" is a risk signal on its own, however good the fingerprint is. So the robot opens the profile's own browser, with its own proxy, visits a list of ordinary sites, scrolls, follows a few links and lets each site store what it normally stores.

The first version did that with JavaScript evaluated in the page. Scrolling was `window.scrollBy` in a loop. Clicking a link or a consent button was `node.click()`. Both did exactly what they said. Cookies arrived, history filled up, the run reported success.

That is why it survived. It worked.

## isTrusted false and scroll detection: how a page sees a script

Every input a person makes reaches the page as a sequence. A wheel notch produces a `wheel` event, then the page scrolls, then `scroll` fires. A click is a `pointermove` trail, `pointerdown`, `mousedown`, `pointerup`, `mouseup`, then `click`, with focus moving on the way. Each of those events carries `isTrusted: true`, because the browser made it.

A script skips most of the sequence. Here is the whole test a page needs:

```
let lastWheel = 0
addEventListener('wheel', () => { lastWheel = performance.now() }, { passive: true })
addEventListener('scroll', () => {
  if (performance.now() - lastWheel > 200) report('scroll without a wheel')
})
addEventListener('click', (e) => { if (!e.isTrusted) report('untrusted click') }, true)

```

A real page would also allow for keyboard scrolling, a dragged scrollbar and touch, so a careful one looks at several signals together rather than one. Our robot failed all of them at once: no wheel, no key, no touch, and the clicks untrusted.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

How it works: Warm-up robot

## Why that was worse than no warm-up

The robot exists to make a profile look used. Instead it wrote a behavioural flag into the history of every profile it touched, on the same sites whose cookies it was collecting. Those sites now held a record of a visitor who scrolled like a script, linked by cookie to the profile that would come back later.

A fingerprint is a set of values. Behaviour is the half that no value hides, and the robot was writing the wrong behaviour into the place the profile's reputation lives. We wrote it down at the time as "a behavioural flag in the jar, called a reputation".

## What replaced it

Every gesture now goes through `human-input.ts`, which sends input at the browser level over the DevTools protocol (`Input.dispatchMouseEvent` and `Input.dispatchKeyEvent`). The page receives the whole sequence with `isTrusted: true`, the same path a physical mouse takes.

Trusted is necessary, not sufficient. A trusted cursor that moves in a straight line at constant speed and lands dead centre every time is still not a hand. So the motion follows the models of real pointing: Fitts' law for how long a move takes, a minimum-jerk curve for its shape, an occasional overshoot and correction on long moves, and sub-pixel tremor on every sample. The cursor also keeps its position between actions, so a click never arrives from somewhere the pointer has not been.

Scrolling is a real wheel. Each profile is a trackpad or a notched wheel, chosen once from its seed, because a session that scrolls like a trackpad on one page and a mouse on the next is describing two computers. A mobile profile gets a finger instead: touch events and a fling, no hover and no wheel at all.

![The Mosaic warm-up dialog for one profile: a preset, a site count and seconds on each site](https://mosaicbrowser.com/shots/warmup.png)
*Warm-up: same browser, same proxy, input through the pipeline*

## We nearly made the same mistake twice

Page control, the part of Mosaic that lets a script or an AI assistant click on a page, needs to know where an element is before it clicks. Its first version called `scrollIntoView` to bring the element on screen and then clicked it with a trusted event. That would have put an untrusted scroll with no wheel immediately before every trusted click: a worse signature than either on its own, because the pair says a script aimed and then pretended.

It now reports whether the element is in view and how far away it is, and moves nothing. The caller turns a real wheel and measures again. For the one case a document scroll genuinely cannot reach, it falls back to a programmatic scroll and says so in the result, as `forced: true`, so nobody mistakes it for the clean path.

## Where the rest of the warm-up stands

The sending path was measured as well as reasoned about. A CDP detector that flips to "bot" when the protocol's `Runtime` domain is enabled stayed clean when input went through the `Input` domain, so the trusted events cost nothing on that side.

Warm-up runs in the profile's own browser, with the fingerprint described in [real fingerprints](https://mosaicbrowser.com/features/real-fingerprints), so the cookies it earns belong to the machine that will spend them. Why separate profiles need separate histories at all, alongside cookies and IPs, is in [how accounts get linked](https://mosaicbrowser.com/guides/how-to-manage-multiple-accounts-without-getting-linked). And when an assistant drives the same robot over MCP, it goes through the same pipeline: [letting an AI assistant run browser profiles](https://mosaicbrowser.com/blog/ai-assistant-runs-profiles) has how that door is held to the window's rules.

## Questions

### How does a page detect a scroll made by a script?

A script scroll fires scroll events with no wheel, touch or key event in front of them. A page that listens for wheel and scroll together sees movement that no pointing device produced.

### Is a click from element.click() trusted?

No. It dispatches one click event with isTrusted set to false and no pointer, mouse-down or mouse-up events around it, so a page that records either can tell it apart from a person.

### How does Mosaic's warm-up scroll and click now?

Through the browser's own input pipeline over the DevTools protocol, so the page receives full trusted sequences: mouse moves, presses, releases and real wheel events, timed per profile.

## Related



[Letting an AI assistant run browser profiles over MCP](https://mosaicbrowser.com/blog/ai-assistant-runs-profiles): Mosaic's MCP server lets Claude or any MCP client create, start and drive browser profiles. Three faults we found when a model was the caller.

[Machines that exist: fingerprints drawn from real hardware](https://mosaicbrowser.com/features/real-fingerprints): Mosaic draws each profile's GPU, screen, CPU, memory and fonts from one real device, so the parts of a fingerprint agree with each other and with the host.

[How to manage multiple accounts without getting them linked](https://mosaicbrowser.com/guides/how-to-manage-multiple-accounts-without-getting-linked): Accounts get linked by shared cookies, IPs, fingerprints and habits. Give each its own profile, exit and history, then test it before you trust it.

[Browser fingerprint: what a site can read about your machine](https://mosaicbrowser.com/glossary/browser-fingerprint): A browser fingerprint is the set of values a site reads about your browser and computer, which together recognize you without cookies. What goes into one.







## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
