Lumo Android: the WebView wrapper is the whole app, and the build matrix runs on two axes
Android application for Proton Lumo
At a glance
- What is it?
- This repository holds no Lumo code, only a native shell around the web application at lumo.proton.me, and its real engineering content is the seam between the two: a bidirectional JavaScript interface, voice entry injected straight into the composer, Play Billing, and a build matrix that ships GMS and no-GMS flavours.
- Who is it for?
- The Lumo Android repository fits someone who wants Proton's assistant on Android with voice entry and Play Billing handled natively, and it fits badly as a place to change how Lumo behaves, since nothing about the assistant's behaviour is in this repository. It serves a privacy audit only partly, because the question of what runs on the device and what runs on the server is answered by the web application rather than by the code here.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 6 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 October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The repository contains no Lumo code, only a shell around it
The framing is stated plainly and it determines everything else about the project. This is the native Android application wrapper for the Lumo web application at lumo.proton.me, with additional features such as voice entry, and Lumo itself is described as Proton's privacy-first AI assistant, from the organisation behind encrypted email, a VPN, a password manager and cloud storage. So the intelligence lives in a web application that is not in this repository, and what is here is the Android shell around it: an activity, a WebView, a permission layer, a billing integration and a set of native conveniences. The architecture section is a diagram rather than prose, and it shows a single activity with Compose navigation, an application class handling startup, a manager layer for edge-to-edge insets, the WebView lifecycle and runtime permissions, and a view model holding UI state, events and network checks. The diagram itself stops partway through that layer, so anything it did not reach is not described. The app is published both on Google Play and on F-Droid under the package name me.proton.android.lumo, and the code is GPL-3.0, which covers the wrapper rather than the service behind it.
Bidirectional JavaScript is the seam between the page and the phone
The interesting engineering is concentrated in one place, which is the JavaScript interface named `WebAppInterface`. It is described as providing bidirectional communication between the web application and native Android code, and three features hang off it. File uploads started from the web interface are handled through `WebChromeClient.onShowFileChooser`, which is how a page that cannot reach the filesystem gets a native picker. The payment dialog is a Compose component triggered through the same interface when a premium feature is bought. And voice input is injected directly into the web application's composer using JavaScript, rather than being typed into a native field. Any review of this app has to start here, because it is where web content acquires the ability to invoke native behaviour, and where the RECORD_AUDIO and BILLING permissions become reachable from a page.
Voice entry is a bottom sheet over the platform recogniser
The speech feature is built from platform components rather than a bundled model. It uses a Material 3 modal bottom sheet for the voice input experience and the native `android.speech.SpeechRecognizer` for capture, with a real-time audio waveform visualisation driven by microphone input levels. Recognised text is then pushed straight into the web application's composer through JavaScript, which is what makes it an addition to the web app rather than a separate input mode. Two details are called out for anyone reading the code. The app documents its own permission handling for RECORD_AUDIO, and there is detection of whether recognition is happening on device, reported for API 33 and above with a status shown to the user. The permission section confirms RECORD_AUDIO is requested for this purpose.
A vosk-model directory sits in the tree with no explanation
The top level contains a `vosk-model/` directory, and nothing in the documentation refers to it. The feature list describes voice input as running on `android.speech.SpeechRecognizer` with on-device detection for API 33 and above, and the permissions section covers microphone access only. That leaves two speech stacks visible in the repository and one documented. Vosk is a nameplate that appears in the tree and nowhere in the prose, which leaves three readings: an offline recognition path that is planned but not yet wired into the app, an experiment that was left behind, or a model bundled for a future release. Nothing in the files that are described here settles it, and it is the kind of question worth asking the maintainers before assuming the app has a fully local voice model.
Two orthogonal axes produce the GMS and no-GMS builds
The variant matrix runs along three dimensions, though only two vary. The environment dimension has a single value, production, pointing at lumo.proton.me, so it exists for naming rather than choice. The debugging dimension has standard, which keeps full WebView debugging, and noWebViewDebug, a GrapheneOS-compatible variant with WebView debugging completely disabled. The services dimension has gms, with Google Mobile Services and in-app billing enabled, and noGms, which drops them and uses an alternative payment dialog. Those combine into the documented commands:
# Standard GMS development build (with WebView debugging)
./gradlew assembleProductionStandardGmsDebug
# NoGMS development build (with WebView debugging)
./gradlew assembleProductionStandardNoGmsDebug
# GrapheneOS-compatible NoGMS build (no WebView debugging)
./gradlew assembleProductionNoWebViewDebugNoGmsDebugThe release variants are `assembleProductionStandardGmsRelease` and `assembleProductionStandardNoGmsRelease`. Notably, all three visible releases carry the nogms suffix in the tag name, so the no-GMS line is what is being tagged publicly.
Signing keys arrive through four variables and a .env you must not commit
Release builds need a keystore and the configuration is entirely environment based, with the same four values documented twice, once in the setup section and again for CI. `LUMO_KEY_ALIAS` is the key alias and defaults to lumo, `LUMO_KEY_PASSWORD` is the signing key password, `LUMO_KEYSTORE_PATH` is the full path to the keystore file, and `LUMO_STORE_PASSWORD` is the keystore password. The recommended route is to copy the example file and source it:
# Option 2: Use the .env file (recommended)
cp .env.example .env
# Edit .env with your actual values
source .envThe example file carries the same defaults with placeholder values and repeats the same four-step usage, ending with the release build command. Both places close with the same instruction: never commit these values, and use the CI platform's secret management instead. Debug builds sidestep the whole question, since signing is not required for `./gradlew clean assembleProductionStandardDebug`.
Six permissions, one of them legacy
The manifest needs are listed and each has a stated reason. INTERNET is for the web content, ACCESS_NETWORK_STATE for connectivity checks, BILLING for the Play Billing integration, and RECORD_AUDIO for speech recognition. READ_MEDIA_IMAGES and READ_MEDIA_AUDIO exist to support file uploads from the web interface, which is the same feature that goes through the file chooser callback. The sixth, READ_EXTERNAL_STORAGE, is labelled as legacy access for API 32 and below, which follows directly from the minimum supported version of 29. The rest of the toolchain is pinned: Android Studio latest stable, compileSdk 35, minSdk 29, Java 17 and Kotlin 2.0.21. One recommended setup step is easy to miss and worth doing first, since the hooks script installs pre-commit checks:
./hooks/install-hooks.shGitLab CI, a Gemfile and fastlane in an Android repository
The top level is a mix that does not obviously belong to one product. There is a `.gitlab-ci.yml` and a `Dockerfile.ci`, so the pipeline runs on GitLab rather than GitHub Actions despite the repository living on GitHub. There is a `Gemfile` and a `fastlane/` directory, which is Ruby tooling normally associated with iOS releases, and a `.devcontainer/` for a containerised development environment. Alongside them sit `buildSrc/`, `baselineprofile/`, `config/`, `metadata/`, `ci/`, `data.json`, `version.properties`, `.locale-state.metadata` and a `CLAUDE.md`. The `Makefile` contains exactly one target, which runs a release branch script with a version argument:
create-release:
bash ci/scripts/create_release_branch.sh $(v)None of that is explained in the README, which covers features, variants, setup, permissions and contributing and stops there. The contributing section is the standard five steps, fork, branch with `git checkout -b feature/amazing-feature`, commit, push, open a pull request, and the closing line credits Kotlin, Jetpack Compose and Material Design 3.
Editorial conclusion
The Lumo Android repository fits someone who wants Proton's assistant on Android with voice entry and Play Billing handled natively, and it fits badly as a place to change how Lumo behaves, since nothing about the assistant's behaviour is in this repository. It serves a privacy audit only partly, because the question of what runs on the device and what runs on the server is answered by the web application rather than by the code here. Four things to note. The wrapper is GPL-3.0 while the service it wraps is not part of the repository, so read the licence as covering the shell only. The build matrix is orthogonal along two axes, and the GrapheneOS-compatible variant switches WebView debugging off entirely, which matters if you had planned to attach a debugger to a production build. A vosk-model directory sits in the tree with no explanation anywhere in the documentation, while the documented speech path is the platform recogniser. And the visible releases are all the no-GMS flavour with the variant appended to the tag, 2.1.4-nogms being the most recent on 1 October 2026, the same day as the last push to main.
Frequently asked questions
What is Lumo?
Lumo is Proton's privacy-first AI assistant, from the team behind encrypted email, a VPN, a password manager and cloud storage. The Android app in this repository is a native wrapper around the Lumo web application at lumo.proton.me that adds voice entry, native file upload handling and Google Play Billing.
Is Lumo actually private?
The project describes Lumo as privacy-first and states that it is created by Proton, while this Android repository contains no model and no inference code at all, since the app renders the web application in a WebView. The only on-device processing described is speech recognition, with on-device detection reported for API 33 and above.
Which build variants does the Lumo Android app have?
They multiply along two axes. The debugging variants are standard and noWebViewDebug, the latter a GrapheneOS-compatible build with WebView debugging completely disabled. The service variants are gms with in-app billing enabled and noGms with an alternative payment dialog, and the environment variant is production only.
What permissions does the Lumo Android app request?
INTERNET for web content, ACCESS_NETWORK_STATE for connectivity checks, BILLING for Play Billing, RECORD_AUDIO for speech recognition, READ_MEDIA_IMAGES and READ_MEDIA_AUDIO for file upload support, and READ_EXTERNAL_STORAGE as a legacy path for API 32 and below.
How do you build the Lumo Android app locally?
With the latest stable Android Studio, Android SDK compileSdk 35 and minSdk 29, Java 17 and Kotlin 2.0.21. Debug builds need no signing and run with `./gradlew clean assembleProductionStandardDebug`, while release builds require four signing environment variables or a sourced `.env` file.
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/protonlumo-android-lumo)