Here's the thing about Android that nobody wants to admit. The whole operating system is open source, top to bottom, and yet the way most people actually get software onto it runs through one company's storefront. That's the tension Daniel's poking at this week.
And he's pointing at the oldest surviving answer to that tension.
Right. Daniel wrote in about F-Droid, which he calls one of the veterans in the push to make Android more open and less commercialized. His framing is that it's got a wider selection than the other third-party marketplaces, though obviously nothing close to Play Store scale. Then he asked four things, and they're all good. First, safety practices for sideloading APKs from alternative sources, including whether VirusTotal scanning actually helps. Second, how the repository-based distribution network works, and why that architecture is what produces all these different front ends in the first place. Third, what the submission process looks like if you're an open source contributor who wants to get your own APK into the catalog. And fourth, whether you can run a personal repo to version-control your own installation base, and whether you can keep a public repo and a private repo going at the same time.
That last one is the one I want to get to, but we have to earn it.
We do. So let's start with what F-Droid actually is, because the framing matters.
F-Droid is a free and open source software repository and client. Not a store in the commercial sense. It's been running since 2010, which makes it one of the oldest alternatives to the Play Store still standing. It predates the entire modern sideloading fight, predates Play Integrity, predates all of it. And the scale gap is real, but it's not a failure. F-Droid hosts thousands of apps. The Play Store hosts millions. That gap exists because F-Droid's inclusion criteria are strict. Free and open source only, built from source, no tracking, no proprietary blobs if they can avoid it.
So the small catalog is the point, not the problem.
It's a filter, and the filter is the product. But here's the architectural fact that unlocks everything else Daniel asked about. F-Droid is not one store. It's a client that reads from one or more repositories. The repository is just a signed index, an XML file, plus a directory of APKs. The client fetches the index, verifies the signature, and presents the apps. There's no central server pushing anything. The client pulls.
Which means the storefront and the catalog are separable.
Completely separable. And that's why there are five front ends for what people think is one store.
Okay, so let's unpack the mechanism, because I think people hear repository and their eyes glaze over. What's actually in the index?
The index is a manifest. For each app it lists the package name, the version, the signing certificate fingerprint, the permissions, and a pointer to the APK file. The whole thing is signed with the repository's key. So when your client downloads that index, it checks the signature against the key it already trusts for that repo. If the signature doesn't match, the client refuses the whole index. Not one app. The whole thing.
That's a hard fail, not a soft one.
Hard fail. And then each individual APK also carries its own signature from the developer. So you've got two layers. The repo vouches for the index, the developer vouches for the APK. And F-Droid's own build process adds a third layer, which is that the APK was compiled on F-Droid's infrastructure from the public source, not handed over pre-built by the developer.
That third layer is the one people don't appreciate. Walk through it.
F-Droid's main repository does not accept pre-built APKs. Full stop. If you want your app in there, F-Droid builds it from your source on their own machines. That's the reproducibility guarantee. The APK you download is the APK that was built from the code anyone can read. That's a fundamentally different trust model from a store that just accepts whatever binary the developer uploads.
And that's also why updates are slower.
Much slower. And why some apps just never show up. If your app depends on a proprietary library, or a Google Play Services component, or a closed blob, it can't be built from source, so it can't be in the main repo. That's the tradeoff. Slower updates, smaller catalog, in exchange for provenance you can actually audit.
Give me a concrete example of that tradeoff, because I think it's easy to nod along without feeling it. What's an app people would actually miss?
Anything that leans on push notifications through Google's Firebase, for one. That's a proprietary dependency, so the main repo can't build it. Some developers work around it with a free alternative like UnifiedPush, but that requires rewriting how the app handles notifications, and a lot of developers just don't bother. So you get a situation where a popular app exists on the Play Store, has an open source codebase, and still can't be in F-Droid's main repo because of one dependency. The source is open. The build isn't reproducible without the blob.
So it's not that the developer is being excluded for ideology. It's that the dependency makes the guarantee impossible.
It's a technical constraint, not a moral judgment. And there are third-party repos that will host those pre-built APKs. They just come with a different trust model, which we'll get to.
So now the front ends. If the repository is just a signed index over HTTP, then anyone can write a client that reads it.
The protocol is open and the index format is standardized. The official F-Droid client is one implementation. Droid-ify is another. Neo Store is another. They all read the same index, they just present it differently and add features.
What does Droid-ify add?
A modern Material interface, for one, which the official client has historically not prioritized. But the feature that matters for Daniel's question is VirusTotal integration. Droid-ify and Neo Store both let you scan a downloaded APK against VirusTotal before you install it. Some of them do it automatically, some give you a share-to-VirusTotal option.
And this is where I want to slow down, because I think people treat VirusTotal as a green light.
It is not a green light. VirusTotal is a heuristic check. It runs the file against a pile of antivirus engines and known malware signatures. It catches the low-hanging fruit. It catches the APK that's already been seen and fingerprinted by somebody else. What it will not catch is a targeted malicious APK, or a new variant that nobody's submitted yet, or anything that's been packed or obfuscated specifically to slip past signature detection.
So a clean VirusTotal result means nobody has caught this yet. Not that it's safe.
That's the honest version. It's a smoke detector, not a fireproof wall. Useful, worth doing, not sufficient.
Then what is sufficient? What's the actual safety practice?
Signature verification. That's the real one. You check that the APK's signing certificate matches the developer's known key. If you installed version two from a developer and version three shows up signed with a different key, that's a red flag, full stop. Either the developer rotated keys, which they'd announce, or somebody swapped the binary.
And at the repo level?
You verify the repository's signing key fingerprint. F-Droid publishes its fingerprint. The client shows you the fingerprint of any repo you add. You compare them. It's a one-time check per repo and it's the thing that prevents a man-in-the-middle from feeding you a fake index.
I want to be honest about something. The first time I opened VirusTotal, I closed the tab. It's a wall of jargon and engine names and I had no idea what I was looking at.
Which is a real problem, because the tool is only useful if people can read the output. The number that matters is how many engines flagged it and whether any of them named a specific family. Forty clean and two flagged as generic is different from forty clean and two flagged as a known banking trojan.
And zero flags is not a pass.
Zero flags is zero information. It means nobody's seen it.
So let's compare the trust models directly, because I think that's the cleanest way to frame it. F-Droid's main repo, you trust because the build happened on their infrastructure from public source. A third-party repo that hosts pre-built APKs, you trust because you trust whoever runs it.
And that's a completely different level of assurance. A third-party repo can be perfectly legitimate and still not give you the reproducibility guarantee, because they're just hosting a binary somebody handed them. The signature might match the developer, the index might be properly signed, but nobody verified that the binary corresponds to the source.
Which is fine if you know the developer and you're getting the APK from their own repo. It's less fine if it's a repo aggregating other people's binaries.
That's the distinction. A developer hosting their own builds, that's a direct trust relationship. A third party redistributing builds, that's a trust relationship with a middleman who may or may not have done any verification.
So the practical hierarchy is, main F-Droid repo at the top, developer's own repo next, third-party aggregator repos at the bottom, and anything you found in a Telegram channel somewhere below the floor.
Below the floor is right. And I'd add one more layer, which is the permission review. Before you install, look at what the app is asking for. A flashlight app that wants contacts and location is telling you something about itself.
It's telling you it's not a flashlight app.
It's telling you the flashlight is a cover story. And that check costs you nothing. It's thirty seconds of reading before you tap install.
Alright, so we've covered consuming. Let's flip it. What if you wrote the app and you want it in F-Droid?
The submission process runs through the fdroiddata repository on GitLab. That's the metadata repository. You don't upload an APK. You submit a merge request with a YAML file describing your app.
Describe the YAML.
It's a build recipe. It has fields for the repo, meaning where your source lives. A builds section that says how to compile it, what build system, what version. AntiFeatures, which is where you declare things like tracking or non-free dependencies. And metadata like the description, the license, the categories.
So the YAML is the instruction manual for F-Droid's build server.
It says, here's where the source is, here's how to build it, here's what it does. And then F-Droid's infrastructure runs that recipe and produces the APK.
And a human reviews it.
A human reviews it. That's the part people underestimate. It's not automated. A reviewer checks the metadata, checks the build recipe, checks that the app complies with the inclusion policy. And it can take weeks. Sometimes months. And the contributor has to be responsive, because the reviewer will come back with questions and if you go quiet, the merge request stalls.
What are the common reasons a submission gets rejected?
Non-free dependencies is the big one. If your app links against something proprietary, it's out. Tracking libraries, same. And then there's the reproducibility question. If the build isn't reproducible, if the same source produces a different binary each time, that's a problem, because it breaks the verification guarantee.
What makes a build non-reproducible? That sounds like it should just work.
It should, and it doesn't, because builds embed things. Timestamps, build paths, random seeds, the order files get packed. Two builds from identical source can produce binaries with different hashes if any of that leaks in. So the recipe has to pin those variables, and that's fiddly work. It's the kind of thing where you don't know it's broken until somebody tries to reproduce your build and gets a different result.
So the barrier to entry is real.
It's real, and it's deliberate. The friction is the filter. If it were easy, the catalog would look like every other store.
Now, personal repos. This is where Daniel's question gets interesting, because he's asking about version-controlling your own installation base.
Right. The fdroidserver tools let you run your own repository. You initialize it with fdroid init, you build or collect your APKs, you run fdroid update, and it generates the signed index. Then you point your F-Droid client at your own repo's URL and it shows up alongside the main one.
And the client can read multiple repos at once.
Multiple repos simultaneously. Each with its own signing key, each trusted independently. So your device can be reading from the main F-Droid repo, a developer's personal repo, and your own private repo all at the same time.
Which answers the public-private question directly. Yes, you can run both.
You can. And the reason it works is that each repo is self-contained. It has its own key, its own index, its own APKs. The client doesn't care that they're related. It just sees three repos and trusts three keys.
So the scenario is, you've got a public repo where you publish stable releases for other people. And a private repo, on a server only you can reach, where you keep your own experimental builds, or pinned older versions, or patched versions of software you depend on.
And the client reads both. So your phone shows the stable version from the public repo and your custom build from the private one, and it treats them as separate apps if the package names differ, or it handles the version comparison if they don't.
The version-control angle is what I find interesting. Because the Play Store model is, you get the latest version, and that's it. There's no pinning.
There's no pinning, and there's no rollback. If an update breaks something, you wait for the next update. With your own repo, you control what version is available. You can hold a known-good build and only advance when you've tested.
That's the thing that would matter to me. Not the freedom angle, the stability angle. I don't want something changing under me while I'm asleep.
Which is most of the day, for you.
It is. And I'd rather run an older version I've verified than a newer one I haven't.
That's the real use case for a personal repo. It's not about ideology, it's about control over the update cadence. And the cost is that you're now responsible for the security updates. If you pin a version, you own the consequences of pinning it.
Which is the honest tradeoff. Decentralized trust means you're the one doing the trusting.
You're the one verifying the keys, you're the one deciding which repos to add, you're the one deciding when to update. The architecture gives you control and it hands you the responsibility at the same time. That's not a bug, that's the whole design.
So the model is robust. The question I keep circling is why it hasn't displaced anything.
Scale, user experience, and the fact that most people don't care about the tradeoffs. They want the app, they want it now, and they want it to update itself. F-Droid asks them to make decisions they'd rather not make.
Which is a fair thing to want.
It is. The Play Store is convenient. That's not nothing.
There's another angle to that, though. Convenience compounds. Every person who defaults to the Play Store makes the Play Store more valuable to developers, which makes it harder for anyone to leave, which makes the default stickier. It's a flywheel.
It's a flywheel, and F-Droid's answer to it is not to compete on convenience. It's to be the thing that exists for the people who need it. That's a legitimate strategy. It's just not a growth strategy.
Hilbert: The key was never the problem. We had a hardware security module, proper thing, rack-mounted, and I was the one who held it. Signed every APK update by hand. Two hundred a week, sometimes more. And the whole time, the actual risk was me. Me.
Two hundred a week.
Hilbert: Give or take. This was a small store, one of the alternative ones, and it folded eventually. But the thing I remember is the build pipeline. There was a compromise, somebody got into the build server, and a colleague of mine signed an APK that had been tampered with. He didn't know. The key was secure. The process around it wasn't.
The signature was valid and the binary was malicious.
Hilbert: Valid signature, wrong binary. Nobody caught it for three days. That's the part people don't think about. The cryptography works. The cryptography was never the weak point. It's the hand that turns the key, and whether that hand knows what it's signing.
Which is the same problem F-Droid solves with the build server.
Hilbert: It's a better model than what we had. I'll say that. Building from source on your own infrastructure, that removes the human from the signing step. But it doesn't remove the human from the build step. Somebody still writes the recipe.
Somebody still reviews it.
Hilbert: Somebody still reviews it. Which is why the review takes weeks. That's not slowness, that's the actual product. I tried to set up a personal repo once. Gave up. The documentation was written by people who already knew how it worked. Every step assumed you'd done the previous step. I got as far as generating a key and then I couldn't figure out where the index was supposed to live.
Where's the module now?
Hilbert: On a shelf. It's a brick. Nothing to sign anymore, so it just sits there. I keep it as a reminder. The key is only as good as the hand that turns it. Anyway, I've got the iron on at home, so.
The part he's describing is the exact reason F-Droid's build model exists. Every layer of that story is a failure mode the from-source build is designed to eliminate. The build server compromise, that's the tampered binary. The hand on the key, that's the human signing step. F-Droid moves both of those onto infrastructure that's auditable.
The documentation problem is real. I've bounced off that kind of doc before. It's written for the person who already succeeded.
Which is a genuine barrier. The tools work. The onboarding doesn't.
It's worth saying that onboarding gap has a cost beyond just inconvenience. If the people who care enough to want a private repo can't get one set up, then the whole decentralized model stays theoretical for them. The architecture supports it. The experience doesn't.
A protocol that only experts can use is a protocol that only experts use. That's the quiet failure pattern of a lot of good open source infrastructure. The capability is there, the path to it isn't.
Let's land this. The most common wrong belief about F-Droid is that it's just another app store.
It isn't. It's a repository protocol with a client. The repository is a signed index over HTTP, anyone can read it, anyone can write a client for it. That's why Droid-ify and Neo Store exist. They're not forks of a store, they're independent readers of a shared format.
The second wrong belief is that VirusTotal makes sideloading safe.
It catches known malware. It does not catch targeted malware. The safety comes from signature verification and build provenance, not from a scan result. Zero flags means nobody's looked, not that nothing's there.
The repository model is more robust than a centralized store. It's also more work, and it asks the user to make decisions most people would rather not make. That's the honest version of why it hasn't displaced anything.
The interesting question going forward is whether Google tightening sideloading restrictions makes this model more important or pushes it further to the margins. Both are plausible. The restrictions make the alternative more valuable, and they also make it harder to reach.
That's the open question. Thanks to Hilbert Flumingtop for producing, and for the hardware security module story.
If you got something out of this one, a review helps other people find the show. This has been My Weird Prompts.
We'll be back soon.