← Blog

Private by architecture: no analytics, no cloud AI, no ads

· Jason @ Swiftfox Software

Every app’s privacy policy says it takes your privacy seriously. That sentence costs nothing to write and nothing to quietly amend in an update later. So instead of promises, I want to walk through how Squirrel Reader is actually built — because the trustworthy version of “we don’t track you” is structural, not aspirational. It’s not that we choose not to look at your data. It’s that we built an app that mostly can’t.

The third-party SDK list has one item on it

Grep our dependencies and you’ll find no analytics or telemetry SDK of any kind. No Segment, Amplitude, Mixpanel, PostHog, or Firebase. No crash reporter — no Sentry, no Crashlytics. No ad SDK, no ad identifiers, and consequently no App Tracking Transparency prompt, because there’s nothing to ask permission for.

The one third-party SDK in the app is RevenueCat, which handles Premium subscriptions. It identifies you by your Squirrel Reader account id — a random-looking UUID, not an email or an ad ID — and it sees your App Store receipt, because that is how subscription billing works. That’s the whole list.

The same posture extends to the desktop. The Chrome extension requests only activeTab, storage, and contextMenus: it can see the one tab you explicitly save, and nothing else about your browsing.

What that costs us

Here’s the honest part. I do not know how many people opened Squirrel Reader today. There is no dashboard. Apple tells us download counts and whatever the opt-in slice of App Store users agrees to share, and beyond that we’re flying with the instruments off.

When the app crashes, no report is sent anywhere. We find out when someone emails support@squirrelreader.com, usually with a description like “it died when I rotated my phone,” and we go reproduce it. The app keeps a small in-memory diagnostics log — 300 events, on a hidden screen, never transmitted — that a user can manually copy and send us if they’re feeling generous. That is our entire observability stack: humans, choosing to talk to us.

This is a worse feedback loop for us, and it’s a deliberate purchase. A crash reporter is a telemetry pipe with good intentions, and a small pipe is still a pipe. We’d rather debug from emails.

AI features that never phone home

Squirrel Reader can summarize an article, pull out its key points, suggest questions worth sitting with, answer questions about it in a chat, and transcribe a podcast episode. None of these send your content anywhere.

Summaries, key points, and questions all run on Apple’s on-device foundation model (Apple Intelligence, iOS 26 and later). The article text is already sitting in the SQLite database on your phone; the model runs against it locally, and the only thing that leaves the device is the generated text, which syncs to your own library so your other devices don’t have to regenerate it.

“Ask” — a chat grounded in the article you’re reading — runs the same on-device model per turn, but I want to call out a difference: it’s not just private, it’s not saved at all. There’s no ask_history column anywhere. A thread lives in memory for as long as the app is open and is gone the moment you relaunch it. I went back and forth on this one, because “your chat history synced across devices” is a nice feature to lose. But a conversation is closer to a thought than a document, and I’d rather you get to have one without wondering if it’s being kept.

Transcription works the same way as the others: Apple’s SpeechAnalyzer runs over the audio file you already downloaded. No microphone is involved, the audio is never uploaded, and there’s no cloud speech API in the loop. Only the text output syncs.

If your device can’t run these models, the features simply aren’t there. We didn’t build a cloud fallback, on purpose — a fallback is exactly where the private version quietly stops being the default.

The one thing that is shared, on purpose

There is exactly one place where output crosses between users: podcast transcripts go into a pool shared across everyone, keyed by the episode’s audio URL. If another listener already transcribed an episode, you get their transcript instantly instead of spending twenty minutes of your battery re-deriving the same text from the same public file.

We think this is fine, and we’d rather explain it than bury it in a policy. A public podcast episode is identical bytes for every listener; its transcript contains nothing about you. The pool key is the audio URL itself, so private or tokenized feed URLs never match anyone else’s. What’s shared is the text of a public episode — not your subscriptions, your listening history, or your notes. (Each transcript does record which account contributed it, so we can moderate the pool.)

Publishers see our server, not your phone

When you save an article, your device doesn’t fetch the page — our server does. It requests the URL, extracts the readable content, strips the clutter, and syncs clean text down to your library. The publisher’s logs show our server’s IP with a user agent ending in SquirrelReader/1.0, not your phone on your home network. The parsed article is sanitized on the server too — scripts stripped, tags allowlisted — so what renders in the reader is local HTML with no tracking pixels and no third-party JavaScript.

Podcasts and video are the honest exception: audio and video stream or download directly from the publisher’s servers to your device, like every podcast app ever made. And if you tap “Open Original,” you’re loading the actual web page, with everything the actual web page does. The reader is the private path; we just won’t pretend the whole internet is.

Delete means delete

Account deletion lives in the app, and it’s real erasure: your auth record, every synced row — items, highlights, tags, collections, reading progress — your mirrored article images in storage, and even your contact on our marketing email list, removed in one operation. Before you go, you can export your entire library as JSON. Your reading list shouldn’t be a hostage.

Why bother

None of this is a hardship for a read-it-later app, which is sort of the point. Your whole library lives on your device anyway, because offline is the default here, not a mode — and a squirrel doesn’t file a report with anyone when it checks on its own stash.

If you ever find anything in Squirrel Reader that contradicts this post, email support@squirrelreader.com. I would genuinely like to know, because no telemetry is going to tell me.