# Network policy

> Every feature that can touch the network, what it sends, and how to keep it off.

URL: https://wmkeyboard.pages.dev/privacy/network/
Section: Privacy & security

The core typing engine has no network code anywhere in it. Prediction,
autocorrect, gesture typing and layouts all behave the same with the radio
off. A set of optional tools do reach the internet, each one only while
you're actively using it. Automatic backup runs on a timer too, if you set
one up. This page is the complete list of what goes where, and
[Network activity](https://wmkeyboard.pages.dev/privacy/network-activity/) shows every one of those
requests as it happens, on your own device.

In the F-Droid edition every address on this page is a setting.
Advanced / Servers lists each service and each
download repository, and the same fields sit on each tool's own page. Point one
at a mirror or at a server you run and the keyboard calls that instead; leave a
field empty and it calls the service's own server. See
[Servers](https://wmkeyboard.pages.dev/reference/settings/servers/).

  privacy/tools-enabled-switches — The Tools screen: every tool has its own Enabled switch, on by default.

## What each tool sends, and when

Every network-touching tool makes a plain `HttpURLConnection` request. No
analytics SDK, no ad network and no background sync sits underneath any of
it.

| Tool | Endpoint | Sends | Fires when |
|---|---|---|---|
| Translate | `translate.googleapis.com` (no key) or `translation.googleapis.com` (with a key) | the text you typed into the panel, up to 2,500 characters, plus the target language | as you type, about 400ms after you stop, only while the panel is open |
| Translate and DeepL Write, with your DeepL key | `api-free.deepl.com` (Free keys), `api.deepl.com` (other keys), or the DeepL server you entered | Translate: the same as above. DeepL Write: the text in the field or the selection, up to 2,500 characters. Both send your key in a header | only after you add a DeepL key under Tools / Translate. Translate as you type in its panel; DeepL Write only when you tap it |
| Grammar check, with [LanguageTool](https://wmkeyboard.pages.dev/smart/grammar/#checkers-harper-and-languagetool) on | `api.languagetool.org` (the public server), `api.languagetoolplus.com` with a Premium account, or the LanguageTool server you entered | the text in the field, cut into pieces of at most 18 KB on the public server, the language code, and your username and key if you set them. Never a password field or text typed in incognito | only after you switch LanguageTool on under Tools / Grammar check / Checkers (it starts off), and then while the Grammar panel is open, after each pause in typing (at least 1.2 seconds), for each check it is the one to answer: with Harper above it, that is every language but English. Fix all and the Fix grammar selection action send their text too. The public server is sent at most 20 requests and 75 KB a minute |
| Translate, with your own translation server | the server address you entered | the same as Translate above, inside a chat request with an instruction to translate, plus your key in a header if you set one | only after you enter a server address under Tools / Translate. Then as you type in the panel, in place of every other translation service |
| Translate, on-device models | `dl.google.com` (through Android's download manager), not in the F-Droid build | nothing you typed: the request names the language model | when you press Download for a language, in the panel or under Tools / Translate |
| Translate, Firefox Translations models | `firefox-settings-attachments.cdn.mozilla.net`, Mozilla's server for the files Firefox downloads, 64-bit phones with Android 9 and later | nothing you typed: the request names the model file | when you press Download for a direction, in the panel or under Tools / Translate |
| Translate, NLLB-200 model | `huggingface.co`, one fixed revision of a CTranslate2 conversion, 64-bit phones with 6 GB of memory or more | nothing you typed: the request names the model file | when you accept its licence and press Download, in the panel or under Tools / Translate |
| Add niqqud, Phonikud model | `huggingface.co`, one fixed revision | nothing you typed: the request names the model file | when you press Download under Languages → Hebrew → Hebrew options |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/), prompt translation | your online translators, the same as Translate above | the prompt, only when it isn't English and no on-device translator could handle it | when you press Generate |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/), Improve with AI | your AI provider, as for the [AI tools](https://wmkeyboard.pages.dev/tools/ai/) | the prompt, with an instruction to rewrite it | only when you press Improve with AI |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/#engines), Pollinations | `image.pollinations.ai` (no key) or `gen.pollinations.ai` (with your key, in a header) | the prompt in English with the style's words, in the address, plus size, seed and safety setting; then nothing more, the picture is the answer | only after you switch Pollinations on in the Engines list (it starts off), and then when you press Generate with it picked |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/#engines), AI Horde | `aihorde.net`, then the picture from the Horde's storage (`*.r2.cloudflarestorage.com`) | the prompt and negative prompt, size, seed, count and safety setting, and your Horde key or the anonymous one; then a status check every 4 seconds while it waits in the queue | only after you switch AI Horde on (it starts off), when you press Generate with it picked. Leaving the screen withdraws a queued request |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/#engines), keyed services | `api.openai.com`, `generativelanguage.googleapis.com`, `api.x.ai`, `api.stability.ai`, `api.bfl.ai`, `external.api.recraft.ai`, `api.replicate.com`, `fal.run`, `api.together.ai`, `router.huggingface.co`, `api.cloudflare.com`, whichever you added; FLUX, Replicate and some others hand back a link to their own storage, which is fetched | the prompt in English, the negative prompt where the service reads one, size, seed, count, safety setting, the model name, and your key in a header | only after you add the service under Engines with your key, when you press Generate with it picked (or reached in **One at a time**). **Test** on its screen reads the model list or the account instead, and draws nothing |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/#engines), your own server | the Automatic1111, Forge, SD.Next, ComfyUI or OpenAI-compatible address you entered | the same as the keyed services, plus a checkpoint name, and your key or `user:password` if you set one | only after you add the server under Engines, when you press Generate with it picked |
| GIF search | `api.klipy.com`, `api.giphy.com`, and `gifcities.archive.org` with `web.archive.org` once GifCities is on | your search text, or nothing for the trending feed | immediately when you open the panel (trending), then live as you type |
| Sticker search | same clients as GIF search | same | same |
| Reporting a GIF or sticker result | none of the app's own | nothing: it builds a mail draft naming the provider, your search term, and the result's id and URL | only when you tap Report in the long-press menu, and only your mail app sends it |
| Web search | `api.search.brave.com` | your search query | only when you submit a search, never as you type |
| Image search | `api.search.brave.com` | your search query | same (submit only) |
| Camera, search by photo | `lens.google.com`, `www.bing.com`, `yandex.com` or `tineye.com`, whichever Tools / Camera / Search site names, or your own server for Custom | the photo, scaled to at most 1280 px | only when you press Search on a photo with Search with set to Web. The default, Google Lens, and the share sheet hand the photo to another app and send nothing from the keyboard |
| Dictionary | `kaikki.org`, `en.wiktionary.org`, `api.dictionaryapi.dev`, `api.urbandictionary.com` if you turn it on, in the order set under Tools / Dictionary / Dictionary sources | one English word: your selection, or the word at the cursor | on panel open, if "Look up the word at the cursor" is on (the default); also on manual search. A source is asked only when the ones before it had no entry or could not be reached. Installed vocabulary packs are read on the device first, and send nothing; so are the offline dictionaries, wherever they sit in the list |
| Recorded pronunciations | `en.wiktionary.org` for the list of recordings, `upload.wikimedia.org` for the audio, and `apifree.forvo.com` only if you added a Forvo key | the word, and its language for Forvo | only when you tap an entry's speaker in the Dictionary tool or the Look up card, with **Recorded pronunciations** on (the default). A recording already kept on the phone is played without asking |
| Synonyms | `api.datamuse.com`, `kaikki.org`, `api.dictionaryapi.dev`, in the order set under Typing / Suggestions / Synonym sources | one English word: the word you held on the suggestion strip | only when you tap Synonyms in the word menu, and a source is asked only when the ones before it had nothing. Installed vocabulary packs are read on the phone first, and send nothing |
| Wikipedia | `wikipedia.org` | your search text or an article title | only when you search or open an article, never on panel open |
| [Smart home: Home Assistant](https://wmkeyboard.pages.dev/tools/home-assistant/#home-assistant) | your own Home Assistant server, at the address you typed, over a WebSocket to `/api/websocket` and its web API; `wmkeyboard.pages.dev/ha/` is fetched by *your server* once per browser sign-in, not by the phone | the key Home Assistant issued to the keyboard, in a header; the commands you give (a tap, a slider, an Assist sentence); a camera still when you ask for one. Back come the states of your entities | only while the tool is on and the home is signed in, and then while the panel is open or the keyboard is shown (your choice), plus a minute after. A snippet variable or the AI chat's home tools connect for the moment they need |
| [Smart home: Google Home](https://wmkeyboard.pages.dev/tools/home-assistant/#google-home) | Google's Home APIs through Play services on the phone, which reach Google's service and, at home, the devices directly | what Google's own consent screen granted: the devices of one home, read and commanded through Google's SDK; nothing is sent by the keyboard's own code | same as above; the SDK keeps its own connection to Google while the tool runs |
| [Smart home: SmartThings](https://wmkeyboard.pages.dev/tools/home-assistant/#smartthings) | `api.smartthings.com` (Samsung's cloud); `wmkeyboard.pages.dev/st/` is where the browser lands once per sign-in, and it only forwards the result to the app | the key SmartThings issued, in a header; the device list with its states every ten seconds while the panel is open; the commands you give | same as above |
| [Smart home: Hue bridge](https://wmkeyboard.pages.dev/tools/home-assistant/#philips-hue-bridge) | the Hue bridge on your own network, over HTTPS with its own certificate pinned at pairing | the key the bridge minted, in a header; the commands you give. Back come the lights, rooms, scenes and sensor readings over the bridge's event stream | same as above |
| [Smart home: openHAB](https://wmkeyboard.pages.dev/tools/home-assistant/#openhab) | your own openHAB server, at the address you typed | your API token, in a header; the commands you give. Back come the items and their state changes over the server's event stream | same as above |
| [Smart home: Smart Life and Tuya](https://wmkeyboard.pages.dev/tools/home-assistant/#smart-life-and-tuya) | the Tuya data centre you picked (`openapi.tuyaeu.com`, `openapi.tuyaus.com` and the like) | your project's Access ID and an access token in headers, each request signed with the Access Secret, which never leaves the phone; the device list with its states every 30 seconds while the panel is open; the commands you give | same as above |
| [Smart home: SwitchBot](https://wmkeyboard.pages.dev/tools/home-assistant/#switchbot) | `api.switch-bot.com` (SwitchBot's cloud) | your token in a header, each request signed with the secret key, which never leaves the phone; each device's state once a minute while the panel is open; the commands you give | same as above |
| Sync with [another profile](https://wmkeyboard.pages.dev/privacy/backup/#sync-between-devices) | no server, and not the network: 127.0.0.1 on the phone itself, on a port taken from the pairing code | to the copy of WM Keyboard in your private space or work profile only, after both prove they hold the same pairing code: this copy's sync file, encrypted with a key made from that code | only while sync goes through an Other profile location, and only when both copies are running. In between it keeps one socket open on 127.0.0.1 and sends nothing |
| [KDE Connect](https://wmkeyboard.pages.dev/tools/kde-connect/) | no server at all: computers on your local network, over UDP 1716 and TLS on TCP 1716 to 1764 | a name, a random device id and the list of features, to find computers; then, to a computer you paired, whatever you use the tool for: pointer moves, what you type there, the clipboard, files, media commands, battery level | never while nothing is paired and no device list is open. After that only while the link is allowed to exist (keyboard showing, by default), and to unpaired computers only while a device list is open. A paired WM Keyboard computer also gets the sync file, when **Sync with paired devices** is on. Album art for a player on the computer may be fetched from the web address that player names |
| Currency converter | `open.er-api.com`, falling back to `api.frankfurter.dev`. The F-Droid edition starts from `cdn.jsdelivr.net` (currency-api, which uses `latest.currency-api.pages.dev` when jsDelivr fails) and falls back to `api.frankfurter.dev`. The source is yours to pick from those, `allratestoday.com` and `api.coinbase.com` | nothing identifying: it fetches the whole rate table, converted locally | on panel open, or the first time a smart chip sees an amount. In the F-Droid edition that chip waits for your tap unless **Fetch rates as you type** is on. Cached for hours, not fetched per conversion |
| [Internet speed test](https://wmkeyboard.pages.dev/tools/speed-test/) | `speed.cloudflare.com` by default; a LibreSpeed server you chose instead, your own or one from the volunteers' list at `librespeed.org`, which is fetched when you open the picker | random bytes, down and then up, as many as the line carries in the time set; ten empty requests for the ping; one request that asks the server what network you are on, which the card shows. Nothing you typed | only when you press Start, never on panel open. A run moves tens to hundreds of megabytes, and [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) asks first on a metered connection |
| Cryptocurrency prices | `api.coinbase.com`, falling back to `cdn.jsdelivr.net`; the F-Droid edition uses `cdn.jsdelivr.net` alone. `api.coingecko.com` is the other choice | nothing identifying: it fetches prices for the coins you have turned on, converted locally | only when you type a coin amount or select a coin on the panel, never on a plain panel open. In the F-Droid edition a typed amount waits for a tap on its chip. Cached for a few minutes |
| Weather | `api.open-meteo.com`, `geocoding-api.open-meteo.com` | the location you typed or searched, or nothing beyond a saved lat/long | on panel open, cached for a while, or when a weather chip needs a forecast (in the F-Droid edition that chip waits for your tap unless **Fetch the forecast as you type** is on); geocoding only fires while you're editing the location |
| [Location](https://wmkeyboard.pages.dev/tools/location/) | `nominatim.openstreetmap.org`, or the Nominatim server you set in the F-Droid edition | the shared point at the precision you chose, and the keyboard's language for the answer; a city-level share asks only for a city. Never the raw reading when a coarser precision is set | once per reading and precision, when the panel has a position: after **Locate me**, or on panel open if Android already allows location and **Locate when the tool opens** is on. Also once for **Use my location** on the weather and compass tools, to name the city. Turn **Look up the address** off and the tool sends nothing at all. Map tiles go only to a tile server you name yourself, none by default |
| AI (cloud provider) | the provider you picked, on its own API: Anthropic, OpenAI, Gemini, Grok (xAI), DeepSeek, or Brave (`api.search.brave.com`, which runs a web search with your text before it answers) | the text you ran the action on, plus a system prompt | only when you tap an action, never in the background |
| AI (any other OpenAI-compatible service) | whatever address you type in, for instance OpenRouter, Groq, Together or Mistral | same | same |
| AI (self-hosted Ollama or LM Studio) | a server address you enter on your own network | same | same, over plain HTTP |
| AI (on-device) google | none | nothing leaves the phone | never: this provider has no network path at all |
| [AI suggestions](https://wmkeyboard.pages.dev/tools/ai/#suggestions-in-the-strip) in the strip | your AI provider, as for the actions above | the last 120 characters before the cursor (40 to 500, as you set it), or only the last word; plus a short system prompt | off by default. When on, each time you pause about 0.6 seconds after a word ends; a key press cancels it. Never in a password field, a field that turns suggestions off, incognito, or while Data saver holds cloud AI back |
| Reporting an AI result | none of the app's own | nothing: it builds a mail draft and hands it to your mail app | only when you tap Report, and only your mail app sends it |
| Clipboard link previews | whatever URL you copied | a GET request to that page | off by default; when on, right after you copy a new link |
| QR scan link previews google | whatever URL a scanned code contains | a GET request to that page | off by default; when on, right after a QR code decodes to a URL |
| Voice typing (system recognizer) | Android's platform speech service (not WM Keyboard's own code) | your speech audio | whenever the on-device model for that language isn't installed: always, below Android 12 |
| Voice typing (Whisper) | none | nothing leaves the phone during dictation | never: fully offline once the model is downloaded |
| Voice typing (Whistle) google | none | nothing leaves the phone during dictation | never: fully offline once the model is downloaded |
| Voice typing (Asr4All) | none | nothing leaves the phone during dictation | never: fully offline once a model is downloaded |
| Voice typing (Parakeet, Moonshine, Sherpa, Omnilingual, Vosk) | none | nothing leaves the phone during dictation | never: fully offline once the model is downloaded |
| Glide typing (FUTO Swipe engine) | none | nothing leaves the phone while you swipe | never: fully offline once the two models are downloaded |
| Voice typing ([transcription server](https://wmkeyboard.pages.dev/tools/transcription-server/)) | a server address you enter, on your own network or a service such as Groq or OpenAI | the recording of each phrase (up to 30 seconds, compressed as Opus at about 20 kbit/s, or 16 kHz WAV where the server cannot read that), the model name you set, the layout's language unless you turn that off, and up to 150 of your own words plus your prompt text when [words to listen for](https://wmkeyboard.pages.dev/tools/voice/#words-to-listen-for) are on | once per phrase, after you stop speaking, only while you dictate with this engine |
| [Music ID](https://wmkeyboard.pages.dev/tools/music-id/), online recognisers | `api.audd.io`, the ACRCloud host of your project, `audiotag.info`, `shazam.p.rapidapi.com`, or the recognition server you entered; each only once you switched it on with your key | the clip the microphone recorded when you pressed Listen (6 to 12 seconds, Opus at 20 kbit/s or 16 kHz WAV; the Shazam listing gets the last five seconds raw) and your key for that service. Never in a password field or incognito; held back by data saving's **Song recognition online** row | once per Listen, to each recogniser in turn until one names the song; not at all when the song was read from the player on the phone |
| [Music ID](https://wmkeyboard.pages.dev/tools/music-id/), cover art | `itunes.apple.com`, then `api.deezer.com` | the title and artist of a song already recognised, to fetch its cover and a store link; never the clip | once per song that came without a cover, and only when data saving allows online recognition outright |

The AI panel's **Report** button is the one row that quotes your own text
without any network of its own. It opens a draft in your mail app holding
the generation and the text it ran on, and you read and send it, or don't.
See [Reporting a result](https://wmkeyboard.pages.dev/tools/ai/#reporting-a-result).

Some rows need a note. GIF and sticker search fetch a
trending feed the instant you open the panel, before you've typed anything.
See [GIFs](https://wmkeyboard.pages.dev/emoji/gifs/) for that behavior in full. Currency, weather and
location are the tools that touch the network without you typing a query first.
None sends anything you typed. Currency pulls a public rate table and
converts locally, weather sends only the location you already saved or
searched, and location asks for the address of the point it is about to
share, at that share's precision. Voice typing's network path isn't literally WM Keyboard's own HTTP
code, since it's Android's `SpeechRecognizer` used as a silent fallback, but
audio still leaves the device that way. See [Voice typing](https://wmkeyboard.pages.dev/tools/voice/)
for the full breakdown, or [Offline voice (Whisper)](https://wmkeyboard.pages.dev/tools/whisper/) for
the fully on-device alternative.

Photo backgrounds are not a toolbox tool, but they do reach the network,
so they belong in this list. Open the picker, type a word, and that word
goes to Unsplash and Pexels along with the subject, color and orientation
filters. Nothing else about you goes with it. Thumbnails load from each
service's own servers rather than being copied through anything of ours,
which is how a photographer is credited with the view. Choosing a photo
downloads that one file at keyboard size, and tells Unsplash once that it
was taken, which is how its download counts stay honest. A rotating
background is the one part that fetches without you watching. It tops up a
small set of photos as the keyboard closes, never while you type, and skips
that entirely while the device is locked, under power saving, on mobile data
unless you allow it, and when High contrast keys is on. See
[Online photos and rotation](https://wmkeyboard.pages.dev/themes/online-photos/).

Everything else in the toolbox is offline by design: OCR (apart from the
one-time download of a language's Tesseract or PaddleOCR data, below, and the **Online
model** engine, which sends each scanned photo to the OpenAI-compatible
service you name and is off unless you choose it), QR/barcode
scanning, document scanning, the calculator, unit converter, symbols picker,
password generator, QR code generation, grammar and spell-check, the camera
tool (apart from searching with a photo on the Web setting, above), and every calendar/alt-calendar/qibla/moon-phase/solar-time feature.
See [Scanner](https://wmkeyboard.pages.dev/tools/scanner/#options) and
[Utility tools](https://wmkeyboard.pages.dev/tools/utilities/#details--edge-cases) for their own
confirmations of that.

## The services behind them

  privacy/ai-ollama-plain-http — AI tool settings: a self-hosted Ollama or LM Studio server address, with the plain-HTTP disclosure below it.

"Google Translate" is two different endpoints. With no key, WM Keyboard calls
`translate.googleapis.com`, the same unofficial public endpoint browser
translate extensions use. That's fine for personal volumes, but it's
rate-limited and carries no uptime guarantee. Add a Cloud Translation API
key and it switches to `translation.googleapis.com`, the official, billed
Google Cloud API. See [Translate](https://wmkeyboard.pages.dev/tools/translate/#options) for that switch
in detail.

The full set of third-party hosts a WM Keyboard install can reach, beyond
what's already in the table above:

- **`api.github.com`**, on a build downloaded from the project's GitHub
  releases, is where the update check asks what the newest release is. It runs
  at most once every six hours, and only while you have the settings app open.
  The request carries nothing but a version string identifying the app, and it
  is a conditional request, so an unchanged list of releases sends no data back
  and costs nothing against the limit GitHub applies to everyone sharing your
  IP address. Press **Download** and the APK comes from
  `github.com` (which redirects to `objects.githubusercontent.com`),
  and `raw.githubusercontent.com` serves the release notes when you press
  **What's new**, reading one markdown file out of the tag you are being
  offered and nothing else. That request is made only when you press the
  button. Play Store and F-Droid builds never make any of these
  requests. The notes for the version already installed ship inside the
  app. The one exception, on any build, is a version released before its
  notes were written: opening them then reads that version's file from its
  tag on `raw.githubusercontent.com`. See [Updating the app](https://wmkeyboard.pages.dev/reference/settings/about/#updating-the-app).
- **`f-droid.org`**, on an F-Droid build and no other, is asked what version
  F-Droid has, on the same six-hour schedule. That build downloads nothing: it
  can only point you back at the F-Droid client.
- **`api.keyman.com`** and **`downloads.keyman.com`** are asked for a
  [Keyman layout's](https://wmkeyboard.pages.dev/languages/keyman/#typing-rules) typing rules, only when
  you press its **Typing rules** row or accept the offer to fetch them as you
  switch the layout on. The request names the keyboard and nothing else: the
  first asks for its current version, the second serves the package.
- **`raw.githubusercontent.com/wasi-master/wmkeyboard-data`** is the
  project's own data repository, source for downloadable dictionaries,
  word-pair (n-gram) packs, emoji keyword packs, CJK conversion tables,
  the 5 MB background remover the sticker editor fetches on a phone that has
  no Google Play services to supply one, and a copy of Keyman keyboards'
  typing rules that is read only when keyman.com can't serve them.
  See [Downloads](#downloads-dictionaries-models-and-addons) below.
- **`raw.githubusercontent.com/wasi-master/wmkeyboard-addon-repository`** is
  the sample addon repository, and also where the one-tap **Noto Color
  Emoji** font on the Emoji screen is fetched from (about 10 MB, plus a few
  hundred bytes of version manifest beside it). That one tap doesn't require
  adding a repository first.
- **`raw.githubusercontent.com/wasi-master/wmkeyboard-monkeytype-sounds`** is
  the second pre-added addon repository, a set of key-sound packs. Like the
  one above it is removable, and removing it is permanent.
- **`raw.githubusercontent.com/wasi-master/wmkeyboard-community-addons`** is
  the third, themes and layouts other people sent in. Removable the same way.
  Sending one of your own opens a GitHub issue in your browser; the app itself
  sends nothing. See [Sharing with everyone](https://wmkeyboard.pages.dev/addons/community/).
- **`raw.githubusercontent.com/tesseract-ocr/tessdata_fast`** google
  is the Tesseract project's own language data, pinned to its 4.1.0 release.
  The [text scanner](https://wmkeyboard.pages.dev/tools/scanner/#which-languages-it-reads) fetches one
  file per language, 0.4 MB to 12 MB, only when you press **Download** for
  it. The request names the language and nothing else.
- **`raw.githubusercontent.com/iFleey/PPOCRv5-Android`**
  holds PaddleOCR's PP-OCRv5 models converted for LiteRT, pinned to one
  commit. The [text scanner](https://wmkeyboard.pages.dev/tools/scanner/#which-languages-it-reads)
  fetches its three files, 11 MB together, only when you press **Download**
  for Chinese or Japanese where ML Kit can't read them. The F-Droid build
  fetches only the list of characters from here, and the two models, in MNN's
  format, from the data repository's `models` release. The requests carry
  nothing about you.
- **`huggingface.co`** is the source for offline Whisper, Whistle, Asr4All, Parakeet, Moonshine, Sherpa and Omnilingual speech models,
  the FUTO Swipe glide models, the NLLB-200 translation model, the Phonikud niqqud model and on-device AI models. Most are public. A couple of gated on-device models
  need your own Hugging Face account token.
- **`alphacephei.com`**, Alpha Cephei's site, holds the small Vosk models.
  [Offline Vosk](https://wmkeyboard.pages.dev/tools/vosk/) fetches one zip, about 40 MB for most
  languages, only when you press **Download** for that language. The request
  names the file and nothing about you.
- **`qaihub-public-assets.s3.us-west-2.amazonaws.com`**, Qualcomm AI Hub's
  public bucket, holds Stable Diffusion 1.5 compiled for each Snapdragon
  NPU. The [sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/) fetches the one zip
  for your chip, about 710 MB, only when you pick that model and accept its
  licence, on a Snapdragon 8 Gen 3, 8 Elite or 8 Elite Gen 5. The request
  names the file and nothing about you.
- **`fonts.gstatic.com`** serves Google's Noto animated emoji. Long-press an
  emoji that has an animated version and the keyboard fetches a small WebP
  to loop in the popup, then fetches the full GIF when you tap Send. Nothing
  about you goes with either request. It only happens when Animated emoji is
  on and you're in a field that accepts images. See
  [Animated emoji](https://wmkeyboard.pages.dev/emoji/picker/#animated-emoji).
- **Google's downloadable-font provider (`com.google.android.gms.fonts`)**
  supplies the Noto face the keyboard draws every non-Latin script in, and
  the **Noto Color Emoji** font choice arrives the same way. Play services
  makes that request, not the app, and nothing about you travels with it: a
  font is asked for by family name only. It happens the first time a script
  needs a face the device hasn't already cached, without you choosing
  anything. A device with no Play services falls back to the system font per
  glyph instead.
- **Your backup locations, if you add any.** That's Google Drive
  (`www.googleapis.com`), Dropbox (`www.dropbox.com` to sign in, then
  `api.dropbox.com`, `api.dropboxapi.com` and `content.dropboxapi.com`),
  OneDrive (`login.microsoftonline.com` to sign in, then
  `graph.microsoft.com`), Box (`account.box.com` to sign in, through
  `wmkeyboard.pages.dev/oauth/` on the way back, then `api.box.com` and
  `upload.box.com`), pCloud (`api.pcloud.com` or `eapi.pcloud.com`, and the
  download host it names), MEGA (`g.api.mega.co.nz` and the storage hosts it
  names), iCloud Drive (`idmsa.apple.com` to sign in, `setup.icloud.com`, and
  the iCloud Drive hosts that names), Proton Drive (`drive-api.proton.me` and
  the block storage hosts it names), or the S3, WebDAV, SFTP, SMB, NFS, FTP,
  Git, IMAP, Synology or QNAP server whose address you typed in yourself. A Nextcloud WebDAV location can
  also sign in through your own server's login page. Nothing is contacted until you add a location, so none of
  these are contacted on a fresh install. What travels is your backup file,
  and your sync file if you turn on sync between devices, and nothing else.
  See [Backup & restore](https://wmkeyboard.pages.dev/privacy/backup/).
- **Whatever addon repository you add.** Three are pre-added the first time you
  open the Addons screen, the three named above, but adding a reference doesn't
  fetch anything by itself. See [Installing addons](https://wmkeyboard.pages.dev/addons/installing/) for
  when a repo actually gets contacted.
- **Ollama and LM Studio** are whatever address you point them at, with no
  fixed host. Traffic to a self-hosted server is plain HTTP, and the app
  allows cleartext traffic app-wide to support it, so keep that server on a
  network you trust. See [AI tools](https://wmkeyboard.pages.dev/tools/ai/#self-hosted-servers) for the
  full setup.

None of these services receive anything from WM Keyboard beyond the single
request described for that tool, and there's no shared analytics identifier
tying them together. Automatic backup is the one thing here that fires
without you, and only to the destination you chose yourself.

## Keeping API keys safe

Tools / Translate

Each tool that needs a key has its own field, all blank by default:
Translate's Cloud Translation and DeepL keys, GIF search's KLIPY and GIPHY keys, Web
and Image search's Brave and Tavily keys, photo backgrounds' Unsplash and Pexels keys,
and the AI tool's per-provider keys
(Anthropic, OpenAI, Gemini, Grok, DeepSeek, Brave, and whatever key the
OpenAI-compatible service you pointed it at wants) plus its Hugging Face
token for gated on-device models, and Voice typing's transcription server
key. Leave any of them blank and that tool
either falls back to a free endpoint (Translate) or shows a "needs an API
key" state (GIF, search, AI). See each tool's own page under
[Tools](https://wmkeyboard.pages.dev/tools/overview/) for exactly where its field lives.

Every key you enter lives in the same place as the rest of your settings: a
plain, unencrypted Android DataStore preferences file, protected by Android's
normal app-sandboxed storage rather than by any extra encryption WM Keyboard
adds on top.

Keys are excluded from a manual backup export unless you turn on **Include
API keys**, and from Android's own device backup path they're excluded
unconditionally. See
[Secrets stay out by default](https://wmkeyboard.pages.dev/privacy/backup/#secrets-stay-out-by-default)
for the full export mechanics, including the exact warning text the app
shows you.

Ollama and LM Studio server addresses aren't treated as secrets the way API
keys are. They're not excluded from a manual export, even with "Include API
keys" off. That's usually a LAN address, but if you've folded a token
or password into that URL yourself, it travels in plain exports. Keep that
in mind before sharing a backup file.

## Downloads: dictionaries, models, and addons

Every downloadable asset comes over HTTPS, but the integrity check behind
it isn't uniform. Don't assume every download is checksummed just because
some are.

A large file doesn't come down as one long request. Models, feature
libraries, conversion tables, spellers, interface language packs and app
updates are fetched as up to twelve requests at once, each asking for its
own piece of the file. GitHub, which hosts most of them, holds a single
connection from Bangladesh to between 0.3 and 0.8 MB a second, and twelve
side by side measured about 5 MB a second on a phone; a server that is fast
on one connection, such as Maven Central, is no slower for it. Each request
is its own row in [Network activity](https://wmkeyboard.pages.dev/privacy/network-activity/), to the same
host as before. The pieces are written into a `.part` file with a small
`.ranges` file beside it that records how far each piece got, so a download
that stops, whether you cancelled it, lost the signal or the app was closed,
asks next time only for the bytes it doesn't have. A server that won't answer
a request for part of a file is read in one go instead. None of this changes
the checks in the table: a file is still measured and hashed whole before it
is used. Word lists, word-pair data, emoji keywords and the other small packs
that are unpacked as they arrive still come as one request, because a size
tier stops reading part way through the file and fetching all of it first
would cost more data.

| Download | Source | Integrity check | Confirms before using mobile data? |
|---|---|---|---|
| Dictionaries and wordlists | the project's GitHub data repository | none beyond the gzip stream's own format check | Only from the "download everything for this language" button, which asks while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) is on and its **Downloads** row is set to *Ask each time* (the default) |
| Emoji keyword packs | same data repository | none beyond decode success | Same button, same setting |
| [Offline dictionaries](https://wmkeyboard.pages.dev/tools/search/#offline-dictionaries) for the Dictionary tool | same data repository | the file must carry the header of the dictionary it was asked for before it replaces anything | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| CJK conversion tables (pinyin, kana, stroke, cangjie, jyutping, and the Traditional phrase table), the RIME engine's schema packs, the Mozc engine's dictionary and zinnia's handwriting models | same data repository | SHA-256 verified before the file is used | No |
| [Keyman typing rules](https://wmkeyboard.pages.dev/languages/keyman/#typing-rules) | `downloads.keyman.com`, or the same data repository's copy when keyman.com can't serve them | the file must parse as a keyboard before it's kept, and the data repository's copy is also SHA-256 verified against the checksum listed beside it | No |
| [Keyman layouts](https://wmkeyboard.pages.dev/languages/keyman/#typing-rules) (GitHub and Google Play editions), about 2 MB | the data repository's `layouts` release on GitHub. Fetched by itself once a Keyman layout is on, and tried again when the phone comes back online | SHA-256 of the contents, checked against the value built into the app before the file is used. The server is not trusted at all | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row blocks it on this connection |
| Offline Whisper speech models | Hugging Face | SHA-256 for whisper.cpp models, from a fixed revision; a file-size floor only for LiteRT models | Yes, around 150 MB. See [Offline voice (Whisper)](https://wmkeyboard.pages.dev/tools/whisper/#downloading-and-managing-models) |
| Offline Whistle speech model google | Hugging Face, pinned to one revision | SHA-256 verified before the file is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks. See [Offline voice (Whistle)](https://wmkeyboard.pages.dev/tools/whistle/#setting-it-up) |
| Offline Asr4All speech models | Hugging Face, pinned to one revision per size | SHA-256 verified for both files before the model is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks. See [Offline voice (Asr4All)](https://wmkeyboard.pages.dev/tools/asr4all/#setting-it-up) |
| FUTO Swipe glide models | Hugging Face, `futo-org/futo-swipe` pinned to one revision | SHA-256 verified for both files before they are used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks. See [The FUTO Swipe engine](https://wmkeyboard.pages.dev/typing/glide-typing/#the-futo-swipe-engine) |
| Offline Parakeet and Moonshine speech models | Hugging Face, pinned to one revision each | SHA-256 verified for every file before the model is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks, and Parakeet's 719 MB also asks on mobile data. See [Parakeet](https://wmkeyboard.pages.dev/tools/parakeet/#setting-it-up) and [Moonshine](https://wmkeyboard.pages.dev/tools/moonshine/#setting-it-up) |
| Offline Sherpa speech models, Silero VAD and the English punctuation model | Hugging Face, pinned to one revision each | SHA-256 verified for every file before the model is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks, and a model of 150 MB or more also asks on mobile data. See [Offline voice (Sherpa)](https://wmkeyboard.pages.dev/tools/sherpa/#setting-it-up) |
| Phrase end detectors: Silero VAD for Asr4All, Smart Turn v3.2, and FireRedVAD | Hugging Face for Silero and Smart Turn, pinned to one revision each; FireRedVAD from FireRedTeam's own GitHub repository, `raw.githubusercontent.com`, pinned to one commit. Fetched from the rows under [Phrase end detector](https://wmkeyboard.pages.dev/tools/voice/#hearing-the-end-of-a-phrase), never on their own | SHA-256 and exact size of every file before it is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| Offline Omnilingual speech model (Meta's Omnilingual ASR 300M) and Silero VAD | Hugging Face, pinned to one revision | SHA-256 verified for every file before the model is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks, and its 367 MB also asks on mobile data. See [Offline voice (Omnilingual)](https://wmkeyboard.pages.dev/tools/omnilingual/#setting-it-up) |
| Offline Vosk speech models google | alphacephei.com, one zip per language, each a fixed file name | SHA-256 of the zip, built into the app, checked before it is unpacked; no entry may land outside the model's folder | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks, and a model of 150 MB or more also asks on mobile data. See [Offline voice (Vosk)](https://wmkeyboard.pages.dev/tools/vosk/#setting-it-up) |
| Text scanner languages (Tesseract) google | the Tesseract project's `tessdata_fast` 4.1.0 on GitHub | none beyond the file arriving at the length the server announced; a file Tesseract can't load is not read | Yes, from the scanner's button and from the settings page. See [Scanner](https://wmkeyboard.pages.dev/tools/scanner/#which-languages-it-reads) |
| NLLB-200 translation model | Hugging Face, `JustFrederik/nllb-200-distilled-600M-ct2-int8` pinned to one revision | SHA-256 and exact size of each of its four files before the model is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks; its licence is asked for first. See [Translate](https://wmkeyboard.pages.dev/tools/translate/#on-device-translation) |
| Firefox Translations models (on-device translation) | Mozilla's attachment server, `firefox-settings-attachments.cdn.mozilla.net`, the files Firefox's own Remote Settings point at | SHA-256 and exact size of each download, and again of each file after it is unpacked, against values built into the app | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks. See [Translate](https://wmkeyboard.pages.dev/tools/translate/#firefox-translations-models) |
| [Handwriting models for other scripts](https://wmkeyboard.pages.dev/tools/handwriting/#paddleocr-for-other-scripts) (PaddleOCR), where ML Kit has no model for the language | the data repository's `models` release on GitHub, 7.8 to 13.4 MB per script. Fetched from the handwriting panel's button or the settings row, never on its own | SHA-256 and exact size of both files, checked against values built into the app before they are used | No |
| Text scanner models for Chinese and Japanese (PaddleOCR) | PPOCRv5-Android on GitHub, pinned to one commit; on the F-Droid build, where they read Latin-script text too, the two models (21 MB) come from the data repository's `models` release instead | SHA-256 of each of the three files | Yes, from the scanner's button and from the settings page. See [Scanner](https://wmkeyboard.pages.dev/tools/scanner/#which-languages-it-reads) |
| On-device AI models | Hugging Face, per model | no checksum, a file-size floor only | Yes, at 500 MB. See [AI tools](https://wmkeyboard.pages.dev/tools/ai/#local-models) |
| [Search by meaning](https://wmkeyboard.pages.dev/smart/search-by-meaning/) model (EmbeddingGemma 2) | the data repository's `models` release on GitHub, about 155 MB for text and 220 MB more for pictures. Fetched when you pick it, never on its own | SHA-256 and exact size of every file, checked against values built into the app before it is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/) models: SDXS and the picture filter | the data repository's `models` release on GitHub, about 970 MB together. Fetched when you pick the model and accept its licence | SHA-256 and exact size of every file, checked against values built into the app before it is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| [Sticker generator](https://wmkeyboard.pages.dev/emoji/sticker-generator/) NPU model (Stable Diffusion 1.5) | Qualcomm AI Hub's bucket, `qaihub-public-assets.s3.us-west-2.amazonaws.com`, the zip for your chip from release 0.64.0 | SHA-256 and exact size of the zip before it is unpacked | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| Upscaler networks (waifu2x UpConv7, one for drawings and one for photos) | the data repository's `models` release on GitHub, 2.2 MB each. Fetched the first time you use **Upscale** in the [sticker editor](https://wmkeyboard.pages.dev/emoji/stickers/#the-sticker-editor) or the [photo editor](https://wmkeyboard.pages.dev/tools/gallery/#the-editor), never on its own; a small sticker sent to WhatsApp or Messenger uses the drawing network only once it is already here | SHA-256 and exact size of each file, checked against values built into the app before it is used | No |
| [Neural suggestions](https://wmkeyboard.pages.dev/languages/phonetic/#neural-suggestions) model (IndicXlit) | the data repository's `models` release on GitHub, 13 MB for every language at once. Fetched when you turn the setting on for a language, never on its own | SHA-256 and exact size of every file, checked against values built into the app before it is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| [Romanized dictation model](https://wmkeyboard.pages.dev/tools/voice/#hinglish-banglish-and-other-romanized-languages) (IndicXlit, Indic to Latin letters) | the data repository's `models` release on GitHub, 13 MB for every language at once. Fetched when you tap its row on a language's page, never on its own | SHA-256 and exact size of every file, checked against values built into the app before it is used | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| Addons (themes, icon packs, plugins, sounds, layouts, sticker packs) | whichever repository you added | SHA-256 verified when the manifest supplies one, and mandatory for plugins specifically, since they run code | No |
| [Signal sticker packs](https://wmkeyboard.pages.dev/emoji/stickers/#signal-sticker-packs) | Signal's sticker storage, `cdn-ca.signal.org`. No Signal account is involved, and the request names the pack and nothing about you. The signalstickers.org gallery that the same screen links to opens in your browser, and the keyboard itself never contacts it | every file is checked against the key in the pack link (HMAC-SHA256) before it is opened, so a file that was changed on the way is refused | Yes. The pack preview asks before its first request while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) is on and its **Downloads** row is set to *Ask each time* (the default) |
| [Telegram sticker sets](https://wmkeyboard.pages.dev/emoji/stickers/#telegram-sticker-sets) | Telegram's Bot API, `api.telegram.org`, through the bot you made. Every request carries your bot token, which is how the Bot API knows which bot is asking, and the name of the set. Your own Telegram account is never involved | none beyond the files arriving under the size cap; each is opened as an image, a Lottie animation or a video and dropped if it is none of those | Yes. The set preview asks before its first request while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) is on and its **Downloads** row is set to *Ask each time* (the default) |
| [Rboard themes](https://wmkeyboard.pages.dev/reference/importing-other-keyboards/#gboard-themes-and-rboard-packs) | the Rboard repository on GitHub, `raw.githubusercontent.com/GboardThemes/PackRepoBeta`. The list is fetched when you open **Browse Rboard**, and a pack when you press it. The Google Play build has no Browse Rboard and never makes this request | each pack is SHA-256 verified against the checksum in the list before it is opened; the list itself is capped at 2 MB | Yes. The screen asks before its first request while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) is on and its **Downloads** row is set to *Ask each time* (the default) |
| Feature libraries (GitHub and F-Droid editions) | the native code behind offline voice typing, the compression of recordings sent to a [transcription server](https://wmkeyboard.pages.dev/tools/transcription-server/#how-much-it-sends), grammar and spell checking, the RIME engine for Chinese, the Mozc engine for Japanese, the scanners (text, QR and the built-in document scanner), handwriting (ML Kit's, zinnia's for Chinese and Japanese, and MNN for the other scripts without ML Kit), on-device translation and on-device AI, fetched the first time each feature is used, or all at once from [About / Feature libraries](https://wmkeyboard.pages.dev/privacy/offline/#feature-libraries). The project's own libraries come from the data repository's `natives` release on GitHub. Google's ML Kit, LiteRT and LiteRT-LM libraries come from Google's Maven repository, `dl.google.com`, and Qualcomm's NPU runtime, Microsoft's ONNX Runtime (behind [Offline Sherpa](https://wmkeyboard.pages.dev/tools/sherpa/) and [Neural suggestions](https://wmkeyboard.pages.dev/languages/phonetic/#neural-suggestions)) and Alpha Cephei's Vosk library (behind [Offline Vosk](https://wmkeyboard.pages.dev/tools/vosk/), GitHub edition only) from Maven Central, `repo1.maven.org`. The keyboard reads only the one file it needs out of each package. The F-Droid edition downloads only after you turn on **Download feature libraries**, and the NPU runtime only after you accept Qualcomm's licence. The Google Play edition gets the same parts from Play instead and never makes these requests, and the offline build makes none at all: it takes the same files as [imports](https://wmkeyboard.pages.dev/privacy/offline/#feature-libraries) | SHA-256 and exact size, checked against values built into the app before the file is used. The server is not trusted at all | Only while [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/)'s **Downloads** row asks |
| Addon repository manifests themselves | the repository URL you added | none: it's the trust root, so there's nothing to check it against. Capped at a fixed size instead | No |
| App updates (GitHub builds only) | this project's GitHub releases | SHA-256 verified against the checksum published in the release, and then the APK's signing key is compared against the key your install is signed with. A release with no checksum is refused rather than installed on trust. The small `.dm` baseline profile fetched alongside it is checked against the same checksum file and skipped if it does not match | Yes, and it names the size before it starts |

Only CJK packs, feature libraries, Keyman layouts, the search by meaning and neural suggestions models, app updates, the data repository's copy of Keyman rules, Rboard
theme packs, and addons with a declared hash get cryptographic verification. Wordlists, emoji
packs, Whisper models, and on-device AI models rely on HTTPS transport
integrity plus a basic size check rather than a checksum, and Keyman rules
from keyman.com on HTTPS plus the check that they parse. That matters if you're deciding how much to trust a
third-party addon repository versus the project's own data feed. See
[Installing addons](https://wmkeyboard.pages.dev/addons/installing/#where-each-type-lands) for how
that mandatory plugin checksum is enforced.

## Google components, and what they report

Everything above is WM Keyboard's own code making its own request. Three
Google libraries the app is built on top of talk to Google on their own
schedule, and this is the list of them. None of them can see what you
type. None of them can be switched off from inside the app either, so the
choice is whether to use the feature.

| Component | In which build | Reaches Google when | What it sends |
|---|---|---|---|
| **ML Kit** (handwriting, OCR, QR/barcode scanning, document scanning, sticker background removal, on-device translation) | every build but F-Droid's | when you use one of those features | Per [Google's own disclosure](https://developers.google.com/ml-kit/android-data-disclosure): device make, model, OS build and available ML accelerators; the app's package name and version; a per-install identifier; latency figures, the API settings and input/output sizes; the SDK version, the kind of event (init, model download, detection) and any error codes. Google describes this as diagnostics and usage analytics, says it's encrypted in transit and not passed to third parties, and provides no switch to disable it. |
| **Downloadable fonts** | every build with Play services present | the first time a script's default keyboard font is needed, and whenever you pick a Google font in the theme editor | the font family name and this app's package name, asked of the Play services font provider. Play services makes the actual request, not this app. |
| **Play Store libraries** (in-app updates, and the on-demand downloads of the on-device AI runtime, the on-device translation engine, the interpreter behind offline voice typing and the sticker editor's own background remover, the handwriting recogniser, the libraries behind the whisper.cpp, Asr4All, Parakeet, Moonshine and Whistle voice engines, ONNX Runtime behind the Sherpa voice engine, the compressor for transcription server recordings, the scanner's offline text reader, and the grammar checker) | Play Store builds only | on the update check, when you first use an on-device AI model, when you first pick on-device translation, when you first use offline voice typing or a background removal without Play services, when you first download a handwriting model, when you first dictate with one of those voice engines or a transcription server, when you first download a text-reader language pack, and when you first open the grammar tool or the spell checker starts | the app's package and version, handled by the Play Store app that installed it |

On a Play Store build the update check is the only thing that library is asked, and it is asked only when the app can see that the Play Store installed this copy. Sideload a Play-flagged APK and the app doesn't ask at all, because the answer can only ever be a refusal.

The same builds carry Play's review library, under the same condition. It is asked for the [rating card](https://wmkeyboard.pages.dev/reference/settings/about/#being-asked-for-a-rating) at most once every 120 days. Your word count stays on the phone: the app reads it to decide whether to ask, and sends only the request. Any rating or review you leave goes to Google Play from Play's own card, and the app never sees it.

Google Drive as a backup destination adds a fourth, but only if you connect
it: signing in is a Google sign-in, and after that the app talks to Drive
directly over ordinary HTTPS. Nothing is connected by default.

The F-Droid build ships no ML Kit at all, so the first row doesn't exist
there, and the F-Droid build carries no Play Store libraries either.
The font row is the one that survives everywhere. It's an AndroidX API
talking to whatever font provider the device has, so a phone with Play
services (or microG) on it has that path no matter which build you
installed. A phone without one falls back to the bundled Noto faces and
never asks anybody anything.

## Options

Tools

- **Per-tool Enabled switch** (default **on** for every tool, including the
  networked ones). Turning a tool off here, or from the "Enabled" toggle at
  the top of that tool's own settings screen, removes it from the toolbar,
  the toolbox, and its hardware leader-key letter all at once. A disabled
  tool can't be opened, so it can't reach the network.

Tools / Power saving

- **Background network** is on by default *whenever Power Saving itself is
  active*, and Power Saving is off by default. It stops the fetches that
  happen without you asking for them: clipboard link previews, QR scan link
  previews, and the dictionary's look-up-on-selection. Every tool you open
  by hand keeps working normally, translate, search and AI included. See
  [Power saving](https://wmkeyboard.pages.dev/typing/power-saving/#what-gets-dropped) for the complete
  list of what else it drops.

Advanced / Data saver

- **Data saver** is the network-shaped version of the same idea, and the one
  to reach for if what you're protecting is a data allowance rather than a
  battery. Each feature that fetches gets its own answer: allow it, ask
  first, or turn it off while the connection is metered. The fetches that
  happen on their own default to off, and the ones you start yourself
  default to asking, so a GIF grid on mobile data explains itself and offers
  a **Use mobile data** button rather than coming up empty. See
  [Data saver](https://wmkeyboard.pages.dev/privacy/data-saver/) for the full table.

There's no single "airplane mode for the keyboard" switch beyond those.
The closest equivalent is turning off every networked tool individually, or
turning every Data saver row off and leaving the manual switch on. The
typing engine itself needs no switch at all, because prediction, autocorrect,
gesture typing and layouts have no network code to disable in the first
place.

## Details & edge cases

- **You can build a keyboard with no internet permission at all.** Build it
  with `-Pwmkb.noInternet=true` and the permission is left out of the APK.
  Everything on the device keeps working, and every tool on this page fails
  the way it does offline. See
  [Building](https://wmkeyboard.pages.dev/development/building/#a-build-with-no-internet-permission).
- **The in-app Privacy screen lists the tools, not the fetches.** Its note
  names translate, weather, web search, image search, GIFs, stickers,
  dictionary lookup and the AI tools, and says the network is used only at
  the moment you press one. That covers the deliberate half. It leaves out
  the things that fetch on their own (currency chips, language data, link
  previews) and the download screens, which is what this page adds. See
  [Data collection](https://wmkeyboard.pages.dev/privacy/data/#details--edge-cases) for the same note
  read from the storage side.
- **AI's default provider needs a key before it does anything.** Claude
  (Anthropic) is the pre-selected provider chip, but with a blank key, so
  the tool shows a "needs an API key" state out of the box rather than
  silently trying a provider that isn't configured.
- **Currency and weather cache their results** rather than fetching on every
  open. Currency holds for a few hours, coin prices for a few minutes, and
  weather for about fifteen minutes, so most opens after the first don't
  touch the network at all.
- **Coin prices are fetched lazily.** Cryptocurrency is on by default, but
  the coin source is only asked when you actually type a coin amount or pick
  a coin on the panel. If you never do, your keyboard never talks to it.
- **Only one tool reads your field rather than its own search box.**
  Translate, Wikipedia and both search tools send what you type into the
  panel, and nothing else: the translate panel is its own window and never
  looks at the field behind it. Dictionary is the exception, reading the
  selected or cursor word when auto-lookup is on.
- **Addon repository manifests aren't fetched proactively.** Seeding the two
  pre-added references doesn't contact them. A manifest only downloads when
  you actually open or refresh the Addons screen, which is also the visit
  that seeds them.
- **Cleartext traffic is allowed app-wide**, not scoped to Ollama and LM
  Studio specifically, which is what makes plain-HTTP requests to a
  self-hosted AI server possible at all. It doesn't weaken HTTPS anywhere
  the app picks the address: every tool in the table above is contacted over
  HTTPS, and an addon repository URL on plain `http` is rejected rather than
  quietly upgraded. The places a plain address is your own choice are the
  self-hosted AI server, a Home Assistant home address typed without
  `https://` (which is how most installs answer on the LAN), and an S3 backup
  endpoint or a Git backup server you type with `http://`.

See [Data collection](https://wmkeyboard.pages.dev/privacy/data/) for what the keyboard learns from you
locally, and [Backup & restore](https://wmkeyboard.pages.dev/privacy/backup/) for how API keys and the
rest of your settings move between devices.
