Your secrets stay on your machine — here's how I keep it that way
Dotvault runs telemetry and crash reporting without ever seeing your env files. Here's exactly how that works, and why I bothered.
Dotvault’s entire pitch is that your .env files never leave your machine. No cloud sync, no API, no “sign in to unlock collaboration.” Your secrets stay on your laptop, encrypted at rest, and that’s the end of the story.
So when I tell you that the app also sends anonymous telemetry and crash reports, you’d be right to raise an eyebrow. That sounds like a contradiction. It isn’t — but the only way to prove that is to explain exactly what happens, how it’s built, and where the lines are. So let’s do that.
Why bother with telemetry at all
I’m a solo developer. I don’t have a product team doing user interviews. I don’t have a support inbox full of feature requests (yet). Without some form of usage data, I’m building blind — guessing which features people actually use and which ones I’m polishing for nobody.
That’s where Aptabase comes in. It’s a privacy-friendly analytics service, and it tells me things like: how many people open the Git panel, whether anyone uses the Compare view, which frameworks are most common in people’s projects. Closed-enum events — I get a count of “diff_viewed” and “snapshot_restored”, not a list of what you diffed or restored.
More recently, I added Sentry for crash and error reporting. Before that, the only way I’d know something was broken was if someone emailed me about it. Now, if the app throws an unexpected error and you’ve opted in, I get a sanitised report that tells me what broke and roughly where — without telling me anything about your files or your data.
Both services are behind the same toggle. Off by default. On first launch you see a prompt explaining what gets collected, and nothing is sent unless you explicitly say yes. You can change your mind any time in Settings → Privacy, and it stops immediately — no restart, no “takes effect after you close the app” nonsense.
The line I drew
Here’s the short version of what crosses the wire:
From Aptabase: which features you open, your OS, your app version, and framework names from a fixed public list (things like “Laravel” or “Next.js” — not your project names). Every event is a closed enum. If it’s not on the canonical list, it doesn’t get sent.
From Sentry: the error type (like TypeError), a sanitised stack trace, and your app and OS version. That’s it.
And here’s what never leaves your machine, across both services: environment variable names, environment variable values, file names, project names, absolute file paths, licence keys, email addresses, or anything you’ve typed into the app. Your .env files are walled off from the telemetry code by design — it simply can’t see them.
How the scrubbing actually works
I didn’t just trust the SDK defaults here. Both processes — the main app process and the renderer — run every Sentry event through a shared beforeSend hook before anything touches the network. That hook does several things:
Path scrubbing. Stack traces contain file paths, and in an Electron app those paths can reveal your username, your project directory structure, or the app’s install location. The scrubber strips absolute paths down to relative ones — so a full path through your home directory and project folder gets shortened to just the filename relative to the app bundle. It catches Unix paths, Windows drive paths, UNC paths, file:// URIs, and Electron’s .asar archive paths. Anything it doesn’t recognise gets replaced with [scrubbed-path].
Error message scrubbing. The same path patterns are stripped from error messages too — so if an exception says something like “ENOENT: no such file” followed by a full path on your machine, what I see is the error message with [path] where the location used to be.
Context and tag allowlists. Sentry events can carry arbitrary metadata. The scrubber strips everything except a short allowlist of SDK-provided context (device, OS, runtime info) and tags (process type, release version, environment). Custom contexts, user data, and extra metadata are deleted before the event is sent.
Request data. Electron’s renderer runs in what is essentially a browser, and Sentry’s default browser integrations want to attach the page URL to every event. In Electron, that URL is a file:// path — which leaks your install location. I disable that integration entirely and also strip event.request in the scrubber as belt and braces.
The scrubbing code is shared between both processes, so the rules are identical everywhere. The unit tests cover every path format, the edge cases, and the consent gate — if telemetry is disabled, beforeSend returns null and the event is dropped before it goes anywhere.
The consent model
I was deliberate about making this a single toggle rather than separate ones for analytics and crash reports. Two toggles means twice the decision fatigue, and it creates a weird middle state where you’ve opted into some data collection but not other data collection. One toggle, one promise: either you’re sharing anonymous data or you’re not.
The toggle defaults to off. On first launch you see a prompt that explains both services — what they collect, what they don’t, and where to read more. Declining is one click and the app never asks again. If you opt in and later change your mind, Settings → Privacy, flip the switch, done. It stops immediately.
Read the full details
If you want the exhaustive breakdown — every event name, every closed-enum value, every field Sentry receives — it’s all documented:
- Privacy policy — the legal version, including Sentry and Aptabase as named sub-processors
- Telemetry events reference — the canonical list of every event the app can send
I’d rather over-document this stuff than under-document it. If you ever spot a gap between what’s listed there and what the app actually sends, let me know — that’s a compliance issue, not a cosmetic one.
The honest version
I want to know when things break and which features people care about. You want your secrets to stay on your machine. Both of those things can be true at the same time — it just takes a bit of care in how you build it. That’s what I did, and now you know exactly how.