“Private by design.” “Your data stays yours.” “Offline mode.” Each can describe a different boundary. Start with the vendor’s written data flow, then measure the exact build you run.
Why “private” stopped meaning anything
One product keeps transcription local but uses the network for updates or analytics. Another processes audio remotely and offers retention controls. Both can call themselves private.
These three checks work on any Mac dictation app, including ours. Use them together, and write down the app version, macOS build, any helper processes and your test conditions.
Test 1 — nettop: watch the wire while you talk
nettop ships with macOS and shows every process with an open network connection, live. It is a good first look. It does not decode payloads, and it won’t automatically include a separate helper, extension or system service the app relies on.
~ % nettop every process with a connection ~ % nettop -p 12345 one process: its PID from Activity Monitor
- Open Terminal. Applications → Utilities → Terminal.
- Run
nettop. A live table: process, connections, bytes in, bytes out. - Narrow it to the app. Find the dictation app’s PID in Activity Monitor, then run
nettop -pwith that PID. - Dictate for 30 seconds. Click into any text field and speak steadily, eyes on the counters.
What you should see
Expected for a no-network runtime: once prerequisites are in place, the app process has no connection, or its byte counters stay flat through capture, transcription, cleanup and delivery.
Needs explanation: a connection opens, or its outbound counter climbs, while you dictate. Record the destination and the timing. That correlation is worth investigating, but on its own it does not prove the bytes contain audio.
Test 2 — name the hosts
nettop tells you that an app is talking. A connection monitor tells you to whom. Little Snitch (paid, with a demo mode) and LuLu (free, open source) both show every live connection per app, with the remote hostname.
- Start the monitor, dictate for 30 seconds, then open the dictation app’s entry.
- A no-network runtime shows: no app-owned connection during the tested dictation, once required models are present.
- Needs review: analytics, crash-reporting, API or CDN hosts, especially a connection that starts with capture or transcription.
Hostnames add context. A documented update host at launch is different from an unexplained API or telemetry host that appears when you start talking. Neither the hostname nor the timing decodes the payload, so compare what you see with the vendor’s data-flow documentation and, where you can, an endpoint-firewall or packet-capture review.
Test 3 — blocked network: the blunt instrument
Turn Wi-Fi off and unplug Ethernet. Then run the full loop: open the app, dictate a paragraph into a text field, stop, read the text.
- On-device core: transcription and its disclosed local processing keep working once required models are installed.
- Expected failures: disclosed model downloads, update checks, account sync or online activation may fail while local transcription carries on.
- A failed dictation needs diagnosis: it can mean transcription depends on a service, or it can come from missing models, licensing, microphone access, permissions or a plain bug. Repeat the test and compare it with the documented boundary.
Reading the results: legitimate traffic vs red flags
| Traffic | When | Verdict |
|---|---|---|
| Speech model download | Provisioning or model update | Expected only when disclosed and integrity-checked |
| Update check | App launch or explicit check | Compatible with local transcription when disclosed |
| Crash report | After a crash | Review consent, contents and retention |
| Sustained outbound traffic | During dictation | Investigate destination and payload; timing alone is not proof |
| Analytics or telemetry host | Any time | Contradicts a no-telemetry claim unless explained |
Don’t infer content from byte size alone. The stronger signal is timing plus destination: an app that claims on-device transcription should not need to upload audio while it transcribes.
What we did with LockedIn Flow, and how to check us
The app never downloads models, checks for updates, sends telemetry or calls a hosted service. Its own source has no networking code, and its only entitlement is the microphone. There is no account and no analytics endpoint.
Its speech library, FluidAudio, does include a model downloader. The app switches it off, by setting ModelHub.offlineMode, before any model loads. The speech model is installed once, outside the app, and every file is checked against a SHA-256 compiled into the app. A missing or altered model is never fetched; it’s refused. A source check rejects the common networking APIs and confirms the offline switch is set.
That is evidence about the source. The three tests above check the runtime of the build you actually run. Recognition runs on the Apple Neural Engine through Core ML, and cleanup uses local rules. Contextual cleanup uses Apple’s on-device model on macOS 26 with Apple Intelligence, and it’s off until you turn it on.
Don’t take our word for it. That’s the point of this article. Request a build for your team, install the speech model, identify the app and any helper processes, run nettop, and dictate. If you see traffic you can’t explain, send us the details. The full picture is in the security architecture.
Test procedures reviewed on macOS as of September 26, 2026. App behavior changes between versions, so run the tests against the build you actually have.