Candy Browser: Firefox Extensions on Android, With a Self-Hosted Sync Server
Gesture-first Android browser with Material 3 Expressive design and local privacy tools
At a glance
- What is it?
- Candy Browser is a Kotlin and Jetpack Compose Android browser that runs Mozilla-signed Firefox extensions through GeckoView, with Android System WebView as a second engine and an optional self-hosted encrypted sync server. The interesting parts are the engine choice and the sync protocol; the awkward parts are the Android 13 floor and a sync roadmap that has not shipped multi-user support.
- Who is it for?
- Candy Browser is for Android 13+ users who want uBlock Origin and other Mozilla-signed extensions inside a Compose-native browser, and for people willing to run the Docker-based sync server themselves. It is not for anyone below Android 13, anyone who wants a hosted sync service today (the roadmap lists one as unchecked), or anyone who needs multi-user server support, which is also still an open roadmap item.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The Gap Candy Browser Fills: Extensions and Gestures on Android
Most Android browsers are either system WebView wrappers with no extension model, or full Firefox builds where the gesture layer is whatever Mozilla ships. Candy Browser takes a third position. It defaults to GeckoView, which is the same engine substrate Firefox for Android uses, and it accepts Mozilla-signed Firefox extensions directly inside the app. A fresh Gecko profile arrives with uBlock Origin and "I still don't care about cookies" already present, and the README states that additional compatible extensions can be installed and managed from Candy's browser settings. Extension actions, popups, options pages, permission prompts, updates, and enable/disable toggles are rendered inside Candy's own browser chrome rather than handed off to another surface.
The audience is narrow and specific. You need Android 13 or later, because the repository badges list Android 13+ as the floor. You need to care about content blocking enough to want real extension lists instead of a built-in blocklist. And you need to tolerate a project that ships very frequently: releases v0.38.3, v0.39 and v0.40 all landed within four days in September 2026, which tells you the API surface is still moving.
Two Engines, One UI: How the GeckoView and WebView Split Works
Candy lets you pick the rendering engine per the README: GeckoView for Mozilla-signed Firefox extensions, or Android System WebView for Android's system-managed runtime. The trade is explicit. Choose GeckoView and you get the extension stack, including uBlock Origin. Choose System WebView and you keep what the README calls Toppings and Candy protection, but you give up the Firefox extension path, because extensions in this design are a GeckoView capability.
What makes this more than a settings toggle is where the shared code sits. The README says browser behavior and production UI live in shared Kotlin, and that the address bar, gestures, menus, tab switcher, Hero/Grid/List previews, settings structure, and their motion rules "are not rebuilt for each platform." The top-level repository layout backs that up: there are app/, shared/, and iosApp/ directories side by side, plus a sync/ directory for the server and WebExtension. So the engine is a substitutable backend behind a common Compose UI, not a fork of the whole app.
That is a defensible architecture, but it has a cost the README does not quantify. Two engines mean two sets of rendering quirks, two media pipelines, and two places where a site can behave differently. The documentation points readers to docs/browsing/platform-engines.md for "current compatibility boundaries," which is an admission that the boundary is real and maintained by hand rather than eliminated.
Installing Candy Browser and Loading Your First Extension
There is no package-manager install here. Candy Browser is an Android application, and the README's release badge links to the GitHub releases page, which is where the APK builds live. The README does not document a Play Store listing, an F-Droid listing, or a command-line install path, so treat the releases page as the distribution channel the project itself points at. Check that your device runs Android 13 or later before downloading.
The repository does contain a fastlane/ directory and a keystore.properties.example file, which is the standard shape for a project that produces signed builds from its own CI rather than publishing through a store pipeline. The README does not describe the signing setup, so do not assume anything about build provenance beyond what the release page states.
Once installed, the first real task is confirming the extension stack. The README says a fresh Gecko profile comes with uBlock Origin and "I still don't care about cookies" already installed, so the check is to open Candy's browser settings and look at the extension list before adding anything. Additional Mozilla-signed extensions are installed and managed from those same settings, and their actions and popups stay inside Candy's chrome. If you want to know which extensions are currently known to work, the README directs you to docs/browsing/platform-engines.md rather than promising blanket compatibility.
The other first-run decision is the engine. GeckoView is the default; switching to Android System WebView is what you would do if you want the system-managed runtime and are willing to lose the Firefox extension path. For most readers the default is the right starting point, because the extension support is the reason to pick this browser over a WebView wrapper.
Candy Sync: A Local Outbox, REST Commits, and WebSocket Notifications
The sync design is the most technically specific part of the project, and it is worth reading closely because it is not a generic "sync your bookmarks" feature. The README describes Candy Sync as turning every connected Android, Chromium, or Firefox device into a writable profile: you can open a desktop profile on your phone, inspect its live tab list, open tabs, navigate, pin, reorder, and close them, and the changes flow back to every connected client.
The mechanism is a hybrid. Changes are encrypted locally and written to a durable outbox. REST commits provide ordered, retry-safe storage. Authenticated WebSocket notifications deliver committed changes immediately. If a client is suspended or loses the network, it recovers missing changes through REST. That is a sensible split: the socket is a latency optimization, not the source of truth, so a dropped connection does not corrupt state. It also means the server sees commit ordering and routing metadata even though it cannot read tab contents.
The cryptography is spelled out in the README. The first client creates a random 256-bit workspace key. The immutable passphrase derives a local recovery key with Argon2id. HKDF-SHA-256 derives purpose- and device-specific keys, and AES-256-GCM encrypts and authenticates tab data. Each device also generates its own P-256 private key locally. According to the README, the passphrase, workspace key, private keys, URLs, titles, device names, and icons never reach the server in plaintext; the server stores ciphertext plus the routing metadata sync requires. That last clause matters: metadata minimization is not claimed, only content confidentiality.
Running the Sync Server Yourself
The README gives a short deployment recipe. From the repository root you move into the server directory, copy the example environment file, edit it, and bring the stack up with Docker Compose. The README is explicit that the environment file needs a unique username, password, public URL, and TLS host, and equally explicit that the E2EE passphrase must never be placed in server configuration.
cd sync/server
cp .env.example .env
# Set a unique username, password, public URL, and TLS host in .env.
# Never put the E2EE passphrase in server configuration.
docker compose up --build -dAfter the server is running, the client side has two halves. On desktop you build and load the WebExtension from sync/extension/ and open its browser-managed Options Page, where you enter the endpoint and an E2EE passphrase. On Android you open Settings, then Synchronization, and join with the same endpoint, credentials, and passphrase. The README points to docs/sync/README.md for server deployment, local TLS, Chromium and Firefox loading, Android setup, backups, protocol details, and the full security model.
Two constraints stand out. First, there is no prebuilt server image: the roadmap lists "Publish a prebuilt server image to Docker Hub or a similar registry" as unchecked, so docker compose up --build builds from source on your machine. Second, the extension is not in any store yet, which is also an open roadmap item, so loading it is a manual developer-mode operation. Neither is unusual for a self-hosted tool, but both add setup friction that a hosted product would not have.
Where Candy Browser Is the Wrong Tool
The Android 13 floor is a hard cutoff, not a soft recommendation. Devices on Android 12 or earlier are out, and the README offers no backport path. If you maintain a fleet of older handsets, this browser cannot be part of the plan.
The sync story has a bigger caveat. Multi-user support is an unchecked roadmap item, and so is a hosted offering. That means the current server is effectively single-user, self-hosted software. If you want to share a sync workspace with a family member or a small team, the README does not describe how, and the roadmap implies you cannot yet. Anyone who wants sync without running Docker should wait for the hosted item to be checked.
Extension support is also bounded by Mozilla signing rather than by Candy. The README says Mozilla-signed extensions, and it defers compatibility questions to docs/browsing/platform-engines.md. An unsigned or self-built extension is not described as installable. And because releases are landing every day or two (v0.38.3 on 2026-09-12, v0.39 on 2026-09-14, v0.40 on 2026-09-16), you should expect occasional regressions in a project at version 0.40 rather than treating it as a finished product.
Finally, the iOS target is described in the README as an "iOS WKWebView/Liquid Glass target" with an iosApp/ directory present. That is a target, not a shipped product, and the README does not claim feature parity with the Android build. Do not read the repository layout as a promise of an iOS release.
Alternatives and the Real Difference in Approach
The obvious comparison is Firefox for Android. Mozilla's browser runs GeckoView too and supports a curated extension collection, so the engine substrate is not the differentiator. The difference is the extension surface and the sync model. Firefox for Android restricts which add-ons you can install through its own curated list, whereas Candy's README describes installing and managing additional compatible Mozilla-signed extensions from Candy's settings, with extension UI rendered inside Candy's chrome. On sync, Firefox uses Mozilla's account service, so your tab data flows through Mozilla's infrastructure. Candy Sync is the opposite: a server you run, with a workspace key that the README says never reaches that server in plaintext. If you want zero server operations, Firefox is the easier answer. If you want the server to be yours, Candy is the one making that offer.
The second comparison is a plain Android System WebView shell. Those are simpler and run on far more devices, but they have no extension model at all, which is the entire reason Candy exists. Candy's own WebView engine option is the middle ground: you keep the gesture UI and the local privacy tooling, and you accept the loss of Firefox extensions.
A self-hosted bookmark or tab sync service paired with any Android browser is the third path. It separates the browser choice from the sync choice, which some teams prefer, but it cannot give you uBlock Origin inside GeckoView, because that capability is bound to the browser's engine integration rather than to the sync layer.
Licence, Maintenance and Upgrade Cost
Candy Browser is licensed under MPL-2.0, per the repository and the licence badge in the README. MPL-2.0 is file-level copyleft: modifications to covered files must be made available under the same licence, while larger works that combine MPL files with separate files can be distributed under other terms. For an Android application this mostly matters if you fork and redistribute the app. The README does not state a separate licence for the sync server or the WebExtension, so if you plan to run or modify those components in a commercial setting, confirm the licensing of sync/ and sync/extension/ in the repository itself. This is a description of the licence text, not legal advice.
Maintenance signals are strong on activity and weak on stability. The last push to the default branch was on 2026-09-16, and three releases landed between 2026-09-12 and 2026-09-16. The repository is not archived. That cadence means upgrades are frequent, and the version number, 0.40, tells you the maintainer has not declared an API-stable release. Expect to re-check docs/browsing/platform-engines.md after engine-related updates, because that file is where compatibility boundaries are recorded.
The sync server adds its own upgrade cost. Because the roadmap has not yet published a prebuilt image, every server update means rebuilding from source with docker compose up --build. Backups are covered in docs/sync/README.md rather than in the top-level README, so treat that document as required reading before you put real tab data into a self-hosted instance.
Editorial conclusion
Candy Browser is for Android 13+ users who want uBlock Origin and other Mozilla-signed extensions inside a Compose-native browser, and for people willing to run the Docker-based sync server themselves. It is not for anyone below Android 13, anyone who wants a hosted sync service today (the roadmap lists one as unchecked), or anyone who needs multi-user server support, which is also still an open roadmap item. Before adopting it, verify three things: that your device meets the Android 13+ requirement, that the extensions you depend on are Mozilla-signed and currently compatible per docs/browsing/platform-engines.md, and that you can supply the TLS host and credentials that sync/server/.env requires, since the README warns the E2EE passphrase must never go into server configuration.
Frequently asked questions
What is the Candy Browser app?
Candy Browser is a gesture-first Android browser built in Kotlin with Jetpack Compose and Material 3 Expressive design. It defaults to GeckoView so it can run Mozilla-signed Firefox extensions, offers Android System WebView as an alternative engine, and includes local privacy tools plus an optional self-hosted encrypted sync server.
How do I download Candy Browser for Android?
The README links to the GitHub releases page, which is the distribution channel the project points at; it does not document a Play Store or F-Droid listing. Your device must run Android 13 or later, per the repository's Android 13+ badge.
Does Candy Browser work on iOS?
The README describes an iOS WKWebView/Liquid Glass target and the repository contains an iosApp/ directory, but it does not claim the iOS build matches the Android feature set. Treat iOS as a target rather than a shipped equivalent.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sk2andy-candy-browser)