New app: Lean
Required
- The app complies with the inclusion criteria
- The original app author has been notified (and does not oppose the inclusion) — I am the author.
- All related fdroiddata and RFP issues have been referenced in this merge request — there are none.
- Builds with
fdroid buildand all pipelines pass - There is an issue tracker and contact info of the author so that we can report bugs and contact the author.
Strongly Recommended
- The upstream app source code repo contains the app metadata in a Fastlane folder structure —
fastlane/metadata/android/{en-US,ru}, with title, summary, description, icon, four screenshots and changelogs. - Releases are tagged and auto update is enabled
Suggested
- External repos are added as git submodules instead of srclibs — neither is used. The native sources are pinned tarballs verified by sha256 in
native/versions.lock. - Enable Reproducible Builds — No, I don't want this.
- Multiple apks for native code — the build produces one universal APK. Splitting is possible and I am happy to do it if you would prefer;
-PsingleApkis what currently collapses the split, becausefdroid buildrefuses a build that emits more than one APK.
About the app
Lean is a VPN client for Android with its own interface, routing and subscription handling. It works with any server or subscription and requires no account.
- Source: https://github.com/Th3Nekit/Lean
- License: GPL-3.0-or-later
- Builds the
fossflavour, which carries no promotion of my paid service. The build I distribute myself does.
Answers to your questions
Why libcore from NekoBox rather than sing-box directly?
libcore is the Android binding layer: it wraps sing-box in a Java API through gomobile, and adds the platform plumbing an Android VPN needs — the TUN owner, protect() for sockets, per-app routing, the status/stats interface. Upstream sing-box ships the engine, not those bindings. The app was written against that API from the start; moving to another one is a rewrite of the whole core layer rather than a dependency swap. Both are GPL-3.0, and native/build-linux.sh compiles libcore from pinned sources — nothing prebuilt enters the APK.
Why AmneziaWG-Go, Mieru, Xray and olcRTC as separate components?
Because the core does not implement them.
- AmneziaWG is a modified WireGuard with obfuscation of the handshake (junk packets, magic headers). It is a separate Go implementation with its own JNI glue; sing-box's WireGuard is the plain protocol.
- Xray is needed only for VLESS over XHTTP, a transport sing-box does not have.
- Mieru and olcRTC are independent projects with their own protocols; olcRTC tunnels through a video call.
Each runs as its own process behind a local SOCKS endpoint. All four are compiled from pinned sources by the same script.
Why is NaiveProxy absent?
Only because this flavour does not build it. I did not mean to claim it is impossible, and I have removed that wording from the description. If you would like it included, I will look at what SFA does with cronet and add it in a later version.
The build
native/build-linux.sh runs as the build command, after the scanner, and compiles everything from sources pinned by sha256 in native/versions.lock: the sing-box core through gomobile, AmneziaWG-Go, and the Mieru, Xray and olcRTC helpers.
It needs two NDKs — r25 for the core, because that is what the pinned gomobile expects, and r26b for AmneziaWG-Go. Only one can be declared in ndk:, so the script installs the other through the sdkmanager on PATH, which verifies checksums; it honours ANDROID_NDK_HOME only when that is the version being asked for.
sudo installs patch alone, which the build needs to apply the pinned patches to the core sources.