Skip to content

Build1 publisher3 min readPublished

Page-triggered Chrome AI model downloads cost a multi-profile browser 2.8 GB per profile

Any page calling Chrome's LanguageModel and Translator APIs can pull 2.8 GB into each isolated profile, a developer running a profile fleet found. Chrome's launch flags either expose the block to pages or stop security updates, so the team patched the engine.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Page-triggered Chrome AI model downloads cost a multi-profile browser 2.8 GB per profile
Generated illustration

What happened

  • One synced profile's compressed backup jumped from 56 MB to 2.74 GB overnight, though nobody had logged into anything new.
  • Across 2,068 profiles on the author's laptop sat 82 copies of the speech models and 83 of the translation model, each fetched separately through its own proxy.
  • Running Translator.create() and LanguageModel.create() on example.com after one click added TranslateKit de-en and nano_v3_cpu_component to chrome://components within a minute.
  • Launching with --disable-component-update left both page-triggered installs running while cutting the components kept up to date from 21 to 2.
  • The team's engine patch refuses downloads for four families: Gemini Nano, TranslateKit and its language packs, the SODA speech models and screen AI.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost The site triggers the download and the operator pays for it, at 2.8 GB of metered proxy traffic per profile, or about 280 GB for every 100 profiles that load such a page.
  • constraint Folder whitelists and blacklists only decide what a sync tool uploads; by the time they apply, the proxy traffic and the per-profile disk writes have already happened.
  • decision Anyone running a profile fleet has to choose between carrying an engine patch and paying the per-profile download, because each switch the author measured fails either the fingerprint or the security-update requirement.

Chrome does fetch some components on its own. WasmTtsEngine and OnDeviceHeadSuggestModel are background downloads under a megabyte that turn up in almost every profile [6]. The multi-gigabyte models wait for site code to ask, and their folders land in whichever profile ran it [7]. In ordinary Chrome with one profile, the author wrote, that download is paid once and shared [8]. In a browser running hundreds of isolated profiles, each behind its own residential proxy billed by the gigabyte, it is paid once per profile [9]. The bytes cross the proxy and hit the disk again for every profile, and the user never sees it [9]. On the author's laptop, about 4% of profiles had already pulled the translation model [3].

The backup failure came from how one tool selected files. It packed the whole user-data directory minus a blacklist that had never heard of the model folders [3]. Each browser close uploaded 2.7 GB, and opening the profile on a second machine meant a 30-minute download before the window appeared [3]. Unpacked, the account's cookies, local storage and IndexedDB came to 93 MB, against 3.3 GB of Google's folders, about 97% of the profile [2][2]. The team's own sync packs a whitelist, so those folders never left the machine [4]. The author checked anyway and found the downloads happening in the team's profiles too [4].

The author tested two launch switches against the unmodified browser. Each test read what every API's `availability()` reported, then called `create()` to see what installed [20]. Turning the features off flips `LanguageModel` and `Summarizer` from `downloadable` to `unavailable` [10]. Every profile would then call on-device AI impossible whatever GPU it claimed, and the author treats a difference that uniform across a fleet as a signal the team would be adding itself [10]. Under `--disable-component-update`, the APIs still report `downloadable` [11]. The updates it freezes include the certificate revocation list, Origin Trials, the subresource filter and PKI metadata [11]. "You lose the updates you want and keep the download you were trying to prevent," the author wrote [12].

"So this cannot be a launch flag. It has to live in the engine," the author wrote [13]. The team's rule is that everything a page can observe stays byte-for-byte what it was on the unmodified build [14]. `availability()` answers as before, and the registration and eligibility checks are untouched [14]. When a page calls `create()` or `install()`, the promise stays pending and no `downloadprogress` events arrive [16]. `LanguageModel.availability()` then reports `downloading`, the value stock Chrome gives while a real download is running [16]. The sub-megabyte background components stay, because real Chrome has them and they cost almost nothing [18]. Every other component keeps updating [17]. "The certificate revocation list is not something to trade away for disk space," the post says [17].

I think this is the right design for a fleet that has to pass as ordinary Chrome. The refusal hides inside a state the unmodified build already produces, and no security component goes stale to pay for it [16][17]. A real download does finish eventually. I'd expect a page that times its wait to be the hardest case for this design, since the refused download never progresses [16]. The author's comparison ran old and new builds on macOS with the same persona, launch arguments and click, the same two `create()` calls, and three minutes of waiting [19]. The available text of the post ends before that run's results.

What to watch

  • Results of the author's old-versus-new macOS comparison, which would show whether the patched build fetched any model bytes during the three-minute wait.
  • Whether sites calling the LanguageModel or Translator APIs start timing how long a download sits at downloading; a refusal that never progresses is the patch's weakest point.
  • Whether blacklist-based profile backup tools add the Gemini Nano, TranslateKit and SODA folders to their exclusions.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories