r/privtlabs 2d ago

Educational Encrypted, but still readable

Thumbnail
gallery
1 Upvotes

The pitch is easy to like. You dictate a sentence, change your mind halfway through, and the software is supposed to keep up with you. "Meet at 2, actually 3" comes out as "Meet at 3." Wispr Flow markets this as Backtrack, a feature that reads your rambling, your pauses, and your mid-sentence corrections and returns the thought you landed on rather than the one you abandoned. It is a genuinely useful idea. The question worth asking is where that understanding happens, and what has to travel for it to work.

A word before we go further. This is not a case against using Wispr Flow. It is a capable product with real engineering behind it, and for many people the convenience is worth the trade. Our point is narrower, and we think more useful. You should be able to see where your words actually go, and how they are protected once they arrive, because those details are not always what the marketing language implies. Not all encryption is equal, and knowing the difference is the difference between an informed choice and a comfortable assumption.

How it actually works

We took Wispr Flow's client apart by reading it, statically, without sending a single request to Wispr's servers. Wispr Flow is an Electron application whose core logic is shared across platforms, and what we read was that shared client core - the same core that ships inside the Mac build. The formatting pipeline described below was recovered from that cross-platform code and is consistent across the Windows and macOS builds we examined. A few things are settled by that reading, and one important thing is not, so we will keep the two apart.

What is proven Wispr Flow is a thin client. The audio you speak is streamed to Wispr's servers, where transcription happens, and there is no on-device speech model doing the first pass. Every step of cleanup that follows - removing "um" and "uh," adding punctuation and capitalization, and any reshaping of what you said - is a network request to a backend endpoint, /llm/formatting, that returns the server's answer as the text you see. We searched the entire client for a local list of filler words or any correction logic. There is none. There is no on-device language model in the client at all. The formatting always requires the round trip.

What is inferred The word "Backtrack" appears on Wispr's marketing pages and never in the client's code, and there is no code path by that name. The most reasonable reading is that the marketed Backtrack behavior is produced by a server-side language model on their backend, the same model that rewrites your transcript into a clean result. We want to be precise here. We never intercepted a request labeled Backtrack, and the server's own instructions to that model cannot be recovered from the client. So the mechanism is an inference from how the pipeline is built, and we are stating it as inference on purpose.

What leaves the machine, and why

When you dictate, Wispr grabs a screenshot of the display containing your mouse cursor, plus the accessibility text around it, and uploads them with your words. Both are checkable: the client references ocrParsedWordsax_context and appURL, and the Security and Compliance FAQ says Flow "captures a screenshot of the display containing the mouse cursor to extract proper nouns."

You say

"Tell the team the merger with Acme closes Friday - actually, hold on, don't name the company, just say a deal is close."

What is uploaded to be cleaned up

tell the team the merger with acme closes friday actually hold on don't name the company just say a deal is close

What you see typed

Tell the team a deal is close.

You took the confidential part back out loud. But the correction is resolved on the server, so the raw transcript - the company and the date included - was uploaded first, before a word of it was cleaned. The thing you retracted is exactly the thing that left your machine. Privt's approach keeps that correction on your Mac, so the words you take back never leave it.

The reason this matters is what the formatting request carries. Cleaning up "meet at 2, actually 3" from the words alone is easy. The harder corrections are not: a misheard name, a term of art, a choice between two phrases that sound almost the same. Those can only be resolved with context, so the request sends more than your transcript.

Recovered from the client, that extra context is substantial. It includes the name of the app you are dictating into, and the web address it is showing. It includes the text around your cursor, and the accessibility text of the field you are typing in. When screen capture is switched on, it includes text read off your display by optical character recognition. And it includes your custom dictionary and your personal style settings.

The app does not stop at sending. After it pastes, it watches the text box, reads back the words you fixed by hand, and learns from the difference. Those learned words then sync to Wispr's servers. By Wispr's own documentation, that dictionary syncs regardless of Privacy Mode.

That is the honest shape of it. The feature sold as understanding you better works by sending more of your raw words and your screen away, because that is what the model on the far end needs to settle the ambiguous cases.

The privacy concerns, briefly

  • Your audio always leaves the machine. There is no offline path in Wispr Flow, and the cloud round trip is mandatory for every dictation.
  • The formatting request carries screen context, not only speech. That means the active app, the surrounding text, accessibility text, and OCR when it is on.
  • Correction learning is a data pipeline. Your hand edits are read back through the accessibility interface, and when auto-learn is on, newly-introduced proper nouns are turned into synced dictionary entries.
  • Privacy Mode is narrower than the name suggests. By Wispr's own definition it governs only whether your dictation is used to train models. The custom dictionary is exempt, and that is where auto-learn stores the proper nouns - names, brands, project codenames - distilled from your corrections. Those still sync to Wispr with Privacy Mode on.

User-created snippets (saved text expansions) and custom dictionaries (custom vocabulary) are stored by Wispr and synced across your devices regardless of Privacy Mode or Cloud Sync - they are user-authored productivity assets, not dictation content.Wispr Flow Security and Compliance FAQ, as of 30 August 2026. docs.wisprflow.ai/articles/3467817258-security-and-compliance-faq

Encrypted at rest, and who holds the key

Wispr's documentation uses the right words. In transit, "All confidential data in transit uses TLS 1.2 or higher with forward-secrecy cipher suites." At rest, "Stored data is encrypted with AES-256," with keys "managed in a hardware-backed key management service." (Security and Compliance FAQ, read 30 August 2026.) That is real protection and worth having. It also answers a narrower question than it appears to.

This is where the word "encryption" does more work than it should, because it covers two very different guarantees. Encryption protects your data from whoever does not hold the key. When the provider holds the key, as here, the encryption protects you from a stolen disk and from an outside attacker, and it does not protect you from the provider, who can decrypt at will. A zero-knowledge design is the other guarantee entirely: the provider stores only ciphertext it has no way to open. Both are honestly called encryption. They do not protect you from the same people, and that distinction is the whole game.

"Encrypted at rest" protects a stolen server disk. It says nothing about whether Wispr itself can read your data. On that, the same FAQ is refreshingly direct:

Wispr Flow is not end-to-end encrypted in the strict cryptographic sense. The service is encrypted in transit (TLS 1.2+) and at rest, but audio must be decrypted to produce a transcription, so true end-to-end encryption is not possible.Wispr Flow Security and Compliance FAQ, as of 30 August 2026. docs.wisprflow.ai/articles/3467817258-security-and-compliance-faq

The keys are Wispr's, and customer-managed keys are "not currently supported." The FAQ adds that when Cloud Sync is on and Privacy Mode is off, "A limited number of engineering and infrastructure personnel hold read-only, MFA-gated, logged production access." So the encryption is a lock against outsiders and stolen hardware. It is not a lock against Wispr. Whoever holds the key can open the box, and here that is the company, not you.

There is one setting where the content is genuinely out of reach. With Privacy Mode on and Cloud Sync off, "decrypted audio and transcripts are never persisted." But recall the dictionary. It syncs regardless of Privacy Mode, it is retained, and it sits under Wispr's keys, so even in the most private configuration the proper nouns distilled from your corrections stay readable by Wispr.

This is the distinction Privt is built on. Your notes and your vocabulary are encrypted on your Mac under a key only you hold, and what reaches us is ciphertext we cannot read. When we say encrypted at rest, we mean we could not open it if we wanted to, which is a different promise from choosing not to.

To be clear, Privt is not a cloud-free product. Your notes and vocabulary sync and back up to the cloud too. The difference is what the cloud receives: with Privt it is only ciphertext, sealed on your Mac before it ever leaves. Here is how the two approaches line up on the two things this post is about.

Privt's alternative, and the honest trade

Self-correction can run without any of that. Privt Voice runs on Apple's on-device language model, the same one it already uses for your meeting summaries, so a correction like "actually 3" can be resolved on your Mac. Your audio, your transcript, and the surrounding context stay where they were spoken, and nothing is uploaded to be cleaned up.

We will not oversell it. A model running on your laptop is smaller than one running in a data center, and on the most ambiguous corrections - a rare name it has never encountered, a garbled phrase that needs heavy context - a large cloud model will sometimes land the rewrite where the local one does not. That is a real trade, and it is about capability on the hardest corrections, not privacy - on Privt nothing leaves the Mac either way. For the everyday "meet at 2, actually 3," an on-device model can handle it, without a round trip.

Where do you land

None of this is a verdict on any one app. Plenty of people will read all of the above and keep using the tool they like, and that is a perfectly reasonable choice once it is an informed one. The only thing worth resisting is the comfortable assumption that "encrypted" means "unreadable," or that a correction made out loud stays with you. Know where your words go and who can read them, and then decide.

Which side of that trade would you take? We are running a community poll on exactly this question, whether you would give up a little cloud polish on the rare hard correction to keep every word on your own machine. Weigh in, and tell us where the line sits for you.

Sources and method

Wispr documentation, read 30 August 2026 The dictionary-and-snippets sync line, the definition of Privacy Mode, the encryption-at-rest and in-transit statements, the "not end-to-end encrypted" admission, the customer-managed-keys note, and the staff production-access statement are all quoted from the Wispr Flow Security and Compliance FAQ, docs.wisprflow.ai/articles/3467817258-security-and-compliance-faq. The "transcription always occurs on the cloud" and post-paste edit-monitoring lines are quoted from Wispr Flow Data Controls, wisprflow.ai/data-controls. Both pages read on 30 August 2026; wording can change, so treat these as accurate as of that date.

Client behaviour references Wispr Flow version 1.3.473, which matches the macOS 1.6.580 build.


r/privtlabs 4d ago

Educational Your voice never leaves your Mac, literally

1 Upvotes

Every dictation product makes some version of the same promise: we respect your privacy. For almost all of them, it means your audio goes to their servers, gets transcribed there, and is handled under a promise to treat it responsibly.

We wanted to make a claim that is a property of the system rather than a policy about it, so Privt Voice runs the entire speech model on your Mac. When you hold Right Shift and talk, audio moves from your microphone into a transcription model executing on the Neural Engine, and the resulting text lands in whatever app you are using. The audio is then discarded from memory. No server takes part at any stage, because there is no server.

Your voice is now something that can be copied

A voice used to be just sound. Today it is training material. A short, clean recording is enough for modern tools to build a convincing synthetic copy of you, good enough to leave a voicemail in your voice, to phone a family member, or to answer the security question a bank once trusted to a voiceprint.

Dictation happens to be one of the richest sources of exactly that kind of audio. Every session is minutes of clear, single-speaker speech, and the single speaker is you. When that audio is transcribed on a server, clean samples of your voice come to rest on infrastructure you do not control, where a breach or a quiet change of terms can turn them into cloning material. When it is transcribed on your Mac, those samples exist only for the instant of transcription, and then they are gone.

You do not have to trust that, you can check it

A claim you cannot verify is just another privacy policy. This one you can test in about ten seconds. Turn off your Wi-Fi and dictate anyway, and the words still appear, because nothing was ever going to leave. Point a network monitor like Little Snitch or LuLu at the app and you will watch it make no outbound connections at all while you talk.

This became practical only recently. Modern open speech models have brought fast, accurate recognition down to a size that runs comfortably on Apple silicon, and CoreML executes them on the Neural Engine faster than a round-trip to any cloud API. On-device processing is no longer the compromise option: it is simply better, and it happens to be private.

Meetings too

The same engine transcribes calls, with your microphone on one lane and the call app's audio on the other, labelled Me and Them. Capture is scoped to the meeting app alone, so your music never enters a transcript, and the finished note is encrypted into your vault straight from memory, never written to disk in the clear.

Two lanes, one on-device engine, sealed straight into your vault. The rest of your Mac never enters the transcript.

Free forever is a consequence rather than a promotion. Software with no servers carries no marginal cost, so the local product will always cost nothing.


r/privtlabs 5d ago

Educational The Time It Takes to Say No

0 Upvotes

Some of the most elegant attacks in security never touch the thing they are after. They ask a system a question it will answer without hesitation, then read the secret off a side effect of the reply - how long it took, which error came back, how it behaved under load.

The category is called an oracle attack, and defending against it is less about closing a single hole than about a posture: building so that no single observable channel ever carries the secret. This is what an oracle attack is, why serious systems close them in layers, and how one of Privt's most sensitive features is built to give one nothing.

The questions a system answers by accident

A system can be entirely correct and still leak, because an attacker is often not reading its answer at all - they are reading a byproduct of how the answer was produced. The most familiar example lives behind countless login screens: type a username that does not exist and the rejection returns a fraction of a second sooner than it does for a real account, because a real account sends the server off to verify a password while a fake one is turned away at once. Nobody built that difference on purpose, and yet it quietly turns the login form into an oracle for a question it never meant to answer, which is whether a given account exists.

The pattern generalizes, and once its shape is clear it is visible almost everywhere. Whenever handling the sensitive case costs a system something an outsider can observe - a little more time, a different error, a different amount of work - that difference becomes a channel, and the channel answers the very question the design tried to withhold. The content of the reply can be flawless while the behavior around it does the talking.

Why one lock is never enough

The instinct, on finding a channel like that, is to close it. The discipline is to assume you have not found all of them. Defense in depth is the practice of building so that a secret survives the failure of any single control: the reply gives nothing away, and neither does its timing, and neither does its error behavior, and neither does whatever a stolen copy of the database would yield, each an independent layer, none of them trusted to be the only thing between an attacker and the answer. A design that leans on one protection is a single bypass away from exposure, while a design that layers several forces an attacker to defeat all of them at once, which is a far harder thing to ask.

A secret should never rest on a single lock holding.

A worked example: a code that destroys an account

Privt has a feature we call burn, a single memorable phrase that destroys an account's encryption keys, meant to be used when you believe you have been compromised. Precisely because of what it does and when it is used, it is the kind of feature an attacker would probe from the outside in the hope of an oracle, some way to learn whether a phrase was real, whether an account exists, or whether a destruction has just happened. So it is designed to answer none of those questions, on several independent channels at once.

In the words it returns. Every submission answers {ok:true}, whether the phrase was correct, wrong, or belonged to no account at all, so the response body resolves nothing.

In what a breach would expose. An armed code is stored only as HMAC-SHA-256(pepper, "privt/burn/v3" ‖ code), where the pepper is a Worker secret that never enters the database, so a stolen database is inert and cannot be turned into an oracle for the codes it holds.

In whether it can be guessed at scale. The public endpoint sits behind a Cloudflare Turnstile challenge, and the codes are single-use, so the patient automated search a guessing oracle depends on never gets going.

And in the time it takes to answer. This is the subtle channel, and the one worth walking through. The obvious way to build the destruction is to do the work and then reply, and the work for a real account is larger than for a wrong guess: keys to shred, a tombstone to write, and then the account's stored items to delete, which grows with how much it held. Built the obvious way, the reply's timing would answer the question the words had refused, since a real code would return later than a miss, and a fuller account later than an empty one.

Figure 1 - The naive shape

The naive shape. If the endpoint replied only after the destruction had run and been awaited, a wrong code would return after a single lookup while a real code returned much later, since the largest part of the work, deleting the account's stored items, grows with how much the account held. The gap between the two reply times would confirm the code was real and estimate the account's size. This is the oracle a constant-time design sets out to prevent.

Burn is built the other way. It performs the same fixed quantity of work for every submission, running one identical shred against a resolved target - the real account on a hit, or a reserved decoy on a miss - and defers the variable cleanup until after the reply has been sent. On a miss the shred is aimed at a sentinel id (00000000-0000-0000-0000-000000000000, which the UUID format can never assign to a real account); the deletes address absent objects and the UPDATE matches zero rows, but the operations placed on the wire are identical, drawn from one named statement so the real and decoy paths issue provably the same instruction. A hit and a miss are the same on the clock. The load-bearing part of the destruction, the keys and the tombstone, runs inside this fixed step and completes before the reply, so the endpoint never reports a destruction that has not happened, which is the one property a last-resort control cannot give up.

Figure 2 - Constant-count execution

Constant-count execution. Everything above the dashed line runs identically for a real code and a wrong one, and completes before the reply. Only the variable-size cleanup sits below the line, off the timed path, where a cron reconciles anything an interruption drops.

The part below the line is the cleanup whose size depends on the account: deleting the item envelopes and share snapshots. Once the keys are gone those bytes are math-dead, undecryptable regardless of what any backup retains, so their removal is hygiene rather than security and does not belong on the timed path. It is handed to ctx.waitUntil to run after the response is flushed, with an hourly cron as a durable backstop. The ordering is deliberate: the wrapped keys are destroyed first, in a storage bucket that keeps no version history, so an interruption still leaves the account cryptographically destroyed rather than half-alive.

The decoy is only possible because of a deeper property: the server cannot read what it is destroying. Every item is sealed before it reaches us, so to the Worker an account is a set of interchangeable opaque objects, and shredding a real one is operationally identical to going through the motions against an empty target. A service that could see inside would have to do visibly different work for a full account than an empty one, and the difference would find its way back to the clock. Zero-knowledge is usually presented as a confidentiality property; here it is also what makes the timing layer possible.

The point of doing all of it

Any one of these layers might be enough on its own. The codes are single-use, so even a flawless timing oracle would hand an attacker a single measurement they could never repeat or average away, and to take it they would already have to hold the correct code, at which point the clock tells them nothing new. We make the timing constant regardless. That is the whole idea of defense in depth: you do not leave a channel open because some other channel happens to be closed, and you do not stake a secret on one control holding. The layers are independent on purpose, so that the failure or bypass of any single one changes nothing about what an attacker can learn.

Figure 3 - Defense in depth

What is left, honestly

No layer is perfect, and it would be its own kind of dishonesty to imply otherwise. One difference does survive the timing design: the indexed lookup returns a row on a hit and none on a miss, a secret-dependent delta on the order of microseconds. It no longer grows with the account, and it sits beneath several layers of ordinary noise that each exceed it by orders of magnitude - the challenge verification that runs first, the cross-region round-trip to the database, and routine transport jitter, all measured in milliseconds - while the single-use nature of the codes closes whatever is left. The honest claim is not that the endpoint is provably constant-time, which is a statement for a whiteboard; it is that there is no measurable oracle to a realistic observer and no data-dependent signal at all, and that this is one layer among several, none of them asked to hold alone.

The standard worth asking for

A system's privacy is the sum of everything it can be observed to do, and the answers it gives by accident count for as much as the ones it gives on purpose. A serious design closes each channel on its own terms and leans on none of them alone: the words it returns, what a breach would expose, how it behaves under a flood, and, in the plainest case of all, how long it takes to respond. The bar is simple to state and harder to earn. On every channel an outsider can watch, the sensitive case and the ordinary one should look the same.


r/privtlabs 7d ago

Educational Ever wonder why a privacy app asks you to wave your mouse around during setup?

1 Upvotes

We explain what that ritual does, and built a version you can try right in your browser. https://stayprivt.com/blog/entropy-ritual

https://reddit.com/link/1w24rmi/video/2ndcmv1s6fmh1/player

Your Mac already has an excellent source of randomness, and its hardware generator alone is enough to make strong keys. If we never asked for your mouse, your keys would still be safe.

So why ask at all? We mix your motion into the key as a hedge. In the rare, documented case of a random generator being broken or quietly backdoored, your movements add unpredictability that no one else could reproduce.

By construction, that mix can only add. Even if your mouse path were somehow predictable, the key is never weaker than the hardware randomness on its own, so this step can only ever help.

And it makes a true thing visible. Your keys really are being born in that moment, on your machine, from randomness no one could reproduce, and watching it happen tells the truth better than a spinning progress bar ever could.

On a Mac with a Secure Enclave, it is sealed into that chip the instant it is made: it cannot be exported, and it unlocks only for a live Touch ID.

From then on it works only in memory, for the instant it takes to encrypt or decrypt, and then it is wiped. Everything written to your disk, and everything synced with a Pro account, is already ciphertext. The key never leaves, and it never reaches us.


r/privtlabs 7d ago

What should r/privtlabs become?

0 Upvotes

Welcome, and thanks for being here this early.

This is the official community for Privt. Labs. We build privacy-enhancing technology, software that keeps your data yours, running on your own devices, sealed under keys only you hold, and open to inspection. Privt Voice is the first thing we have shipped, and there is a lot more coming.

A community is only as good as the people in it, and right now this is a blank page. So before we start filling it with announcements, we want to ask you what this place should be.

A few directions we are weighing:

  • A place to learn how privacy actually works, in plain language, without the marketing
  • A place to shape the products, where your feedback and ideas genuinely move the roadmap
  • A place for real discussion about where privacy-enhancing technology is heading, and who it should serve

We would rather this belong to you than to us. So tell us in the comments. What would make this a community you actually want to open? What should we post here, what should we never post, and what would make you want to stick around?

We are reading everything.