BitProxy
Personal fork of sing-box for Android (SFA), the Android client of sing-box by SagerNet. All the heavy lifting (the proxy engine, WireGuard, the VPN service and the app itself) is their work; BitProxy only adds a layer on top. It imports WireGuard configs (for example Proton VPN), gives every app and clone its own VPN / Direct / Block switch, and generates, checks and applies the sing-box config inside the app. GPLv3, like upstream.
Upstream isn't stored in this repo. versions.env pins sing-box and SFA, and patches/
holds the BitProxy changes as git format-patch files. They're applied on top of every build.
App id: com.zonicisalive.bitproxy. It installs next to the official SFA app.
Download: Releases. Use
BitProxy-*-arm64-v8a.apk for current phones. After that the app updates itself: it checks
these releases (Settings → App → Automatic Update Check), shows what's new, downloads the
APK for your phone and installs it over the current version. F-Droid isn't offered as an
update source: it only has the original sing-box app.
Features
-
Servers: every server type sing-box supports:
- WireGuard
.conffiles. - OpenVPN
.ovpnprofiles. The username and password are added in the app. - Share links:
vless://,vmess://,trojan://,ss://,hy2://,hysteria://,tuic://,anytls://,socks://,http(s)://,naive+https://andssh://. - Subscription URLs.
- sing-box JSON: single outbounds, or a whole config.
Import files, paste links, or share them to BitProxy. Everything is stored in private app storage only. Pick a server, or choose Fastest (auto) with a check interval of 1–30 min. The Dashboard card shows latency, or "No response" when a server is down.
- WireGuard
-
VPN provider login: get servers without downloading files. Log in, pick locations, and BitProxy writes one server per location. Log out removes the key or device again.
- Mullvad (account number), IVPN (account ID), Private Internet Access (username and password), AirVPN (API key), AzireVPN (username and password), NordVPN (access token from Nord Account → Manual setup), Cloudflare WARP (free, no account).
- PIA forgets keys that stay unused for a while: if a PIA server stops working, choose its locations again to register fresh keys.
-
Share to import: share a server file, links or a subscription URL to BitProxy, or open a file with BitProxy from a file manager. It's imported and applied immediately.
-
Apps: a VPN / Direct / Block switch for each app, clone (user 999) and work profile, on the Dashboard and in the app picker. Rules match on Android user id, so reinstalled apps keep their setting. Blocked apps get no traffic and no DNS.
-
Everything stays in the tunnel: Android's per-app VPN exclude lists are never used, so Always-on with "Block connections without VPN" works for every app. The routing happens inside sing-box.
-
No DNS leaks: VPN apps resolve through the server's DNS inside the tunnel (with other server types: 1.1.1.1 over TCP through the server). Direct apps use normal DNS.
-
Automatic IPv6: on networks with real IPv6, the server's IPv6 address is added to the tunnel. Otherwise it runs IPv4-only.
-
Trusted Wi-Fi: networks where every app goes Direct (blocked apps stay blocked). Needs location permission set to "Allow all the time".
-
Always through VPN: a domain list that uses the VPN even for apps not in the list.
-
Quick Settings tiles: three tiles, each flipping one app between VPN and Direct.
-
New apps: a notification asks "VPN, Direct or Block?" when an app is installed.
-
Safety:
- Apply checks the config with sing-box before using it, and the previous config can be restored.
- The Dashboard shows the Always-on / Block state, and asks before stopping under lockdown.
- Recovery:
- WireGuard servers get a small in-tunnel check: every 3 min with the screen on, every 15 min with it off, never in Doze, and right after unlocking. Two failures in a row rebuild sing-box.
- After 2+ minutes without any network, sing-box is restarted cleanly.
- Every reload (Apply, an app installed or removed) is watched: a hang or a dead tunnel afterwards restarts the VPN in a fresh process.
- If Android kills the app while the VPN runs, a backup alarm starts it again within 30 min.
- libbox is built with a Go whose timers keep counting in deep sleep (as in WireGuard's and Proton's apps), so the tunnel isn't stale after the night.
- Android (seen on OxygenOS 16) can destroy the socket sing-box uses for TCP while the VPN is rebuilt; every new TCP connection was then refused until a restart. The patched sing-tun listens again within 0.1 s.
- Apply with an unchanged config doesn't reload, so connections aren't interrupted.
-
Diagnostics: tunnel, DNS, IPv6 and Always-on state, clone detection, last Apply, and a Recent events log that survives the night. The export removes all private keys.
Phone setup
- Install the APK from Releases
(
…-arm64-v8a.apkfor current phones). Allow notifications. - Settings → BitProxy → Import .conf files (or share them to BitProxy, or log in under VPN providers), then Apps, then Apply.
- Start the VPN from the Dashboard and allow the VPN request.
- Android: Settings → Network (on OxygenOS: Connection & sharing) → VPN → BitProxy ⚙ → Always-on VPN + Block connections without VPN. Turn Always-on off in other VPN apps first.
- Battery: allow background activity / don't optimize. This is required, for example for the automatic restart after a hang.
- Check it: BitProxy → Leak test in a VPN app (server IP) and in a Direct app (your own IP), then Diagnostics.
On OxygenOS, also turn off any setting that switches Wi-Fi off during sleep. BitProxy recovers from it, but the phone has no network in that time.
Troubleshooting
- No internet but the VPN looks on: open Diagnostics and read "Recent events" (network lost/back, restarts, stops), then use Export log. It contains no keys.
- Over USB:
adb logcat --pid=$(adb shell pidof com.zonicisalive.bitproxy). OxygenOS keeps only about 90 seconds of logcat, so the events log is the long-term record.
Layout
| Path | What |
|---|---|
versions.env | Pinned sing-box tag, SFA commit, Go / NDK / gomobile versions |
patches/android/ | BitProxy commits on top of SFA (new code lives in io.nekohasekai.sfa.bitproxy) |
patches/core/ | Commits on top of sing-box (none so far) |
patches/sing-tun/ | Commits on top of sing-tun (sing-box's TUN library): re-listen when Android destroys the TCP socket |
build-release.sh | Fresh clone of pinned upstream, apply patches, build libbox + APK, run unit tests |
.github/workflows/release.yml | The same build on GitHub, signed and published as a release |
.github/workflows/auto-update.yml | Daily: build and release new upstream versions automatically |
setup-android-dev.sh | One-time toolchain setup (Go, JDK 17, Android SDK/NDK, emulator) into $DEV |
run.sh, gw | Build + run on the emulator (never on a connected phone); Gradle with the right toolchain |
check-upstream.sh | Try all patches on a newer upstream: apply, build libbox + APK, run unit tests |
export-patches.sh | Regenerate patches/ from the local bitproxy branches |
prepare-sing-tun.sh | Clones the sing-tun version sing-box requires, applies patches/sing-tun, points sing-box at it (used by every build) |
find-sfa-commit.sh | Finds the sing-box-for-android commit released as a given version (used by auto-update) |
go-boottime.sh | Go toolchain copy whose timers count deep sleep, used to build libbox |
make-keystore.sh | Optional helper to create a release signing key |
build-release.sh needs Go, JDK 17, the Android SDK + NDK (ANDROID_HOME) and gomobile on
PATH. The other helper scripts load $DEV/env.sh, which sets JAVA_HOME, GOROOT,
GOPATH, ANDROID_HOME and the caches; point DEV at your own toolchain folder.
Building a signed APK
Create a key once and back it up; every update must be signed with the same key:
keytool -genkeypair -keystore path/to/bitproxy.keystore \
-alias bitproxy -keyalg RSA -keysize 4096 -validity 36500
Put the passwords in a signing.properties next to it (chmod 600):
KEYSTORE_PASS=...
ALIAS_NAME=bitproxy
ALIAS_PASS=...
Build:
RELEASE_KEYSTORE=path/to/bitproxy.keystore \
LOCAL_PROPERTIES="$(cat path/to/signing.properties)" \
./build-release.sh
APKs end up in build-work/sing-box-for-android/app/build/outputs/apk/other/release/.
Install over USB with adb install -r <apk> (same key = settings kept).
Never copy the key into the sing-box checkout: upstream tracks a file at
app/release.keystore, so git add -A would commit your key. build-release.sh only uses
its own throwaway clone and deletes the copy afterwards.
Release on GitHub
- Repository secrets:
KEYSTORE_BASE64(base64 -w0 bitproxy.keystore),KEYSTORE_PASS,ALIAS_NAME,ALIAS_PASS. - Actions → Release → Run workflow (or push a
v*tag). APKs appear under Releases.
Automatic updates
auto-update.yml runs every day (and on demand under Actions → Auto update):
- It looks for a new stable sing-box tag (for example
v1.14.3). It waits until the app'smainbranch reaches the same version (a "Bump version 1.14.3" commit with thatVERSION_NAME). sing-box-for-android has no tags of its own. - It builds on that pair with the patches in this repo: libbox, unit tests, a signed APK.
- Success: it publishes a release with the same tag as sing-box (
v1.14.3) and commits the new versions toversions.env. - Failure (a patch conflict or a failing build/test): it opens an issue "BitProxy patches fail on sing-box v1.14.3" with a link to the log, and publishes nothing.
Beta track: sing-box pre-releases (alpha, beta, rc) of a version with no stable release
yet are built on the app's dev branch and published as GitHub pre-releases, only once
the app has that exact version. In BitProxy, Settings → App → Update Track: Stable gets
only stable releases; Beta also gets these pre-releases. A failing Beta build opens an
issue and never affects Stable. versions.env always stays on stable.
Every release lists what's new since the previous one (this repo's commits, plus the new sing-box version). The app's update prompt shows that list.
Manual runs of Release use the tag from versions.env, or v1.14.2-r<run> when that
release already exists (patch changes on the same upstream).
Upgrading to a new upstream release by hand
- Try it first:
./check-upstream.shchecks the newest sing-box release with SFAmain../check-upstream.sh v1.15.0 devchecks a specific pair. It says whether the patches apply, the build works and the tests pass, and shows where it failed if not. Tested: all patches apply and pass on sing-box 1.15.0-alpha.8 + SFAdev, and sing-box 1.15 accepts every config BitProxy generates. - Read the sing-box changelog for removed or deprecated config fields. Update
ConfigGeneratorand the golden tests if anything BitProxy writes changed. - In the local checkouts:
git switch -c bitproxy-new <new tag / commit>andgit am -3 <this repo>/patches/…/*.patch. Fix conflicts, rebuild libbox, and run the unit tests. For sing-tun, the base is the version in sing-box'sgo.mod(prepare-sing-tun.shandexport-patches.shread it). - Update
versions.env, run./export-patches.sh, commit.
Testing checklist (before each release)
- Unit tests and release lint pass (
./gw :app:testOtherDebugUnitTest :app:lintVitalOtherRelease). - Emulator:
- fresh install, import confs, pick apps, Apply, then start without reopening the app
- reboot with Always-on
- a second user (
adb shell pm create-user test) - reinstall a listed app
- switch networks
- a 3-minute outage (
svc wifi disable; svc data disable): the events log shows "network back … restarting VPN" - server down, IPv6 on and off
- Phone:
- an overnight run
- leak check: a VPN app shows the server IP, a Direct app your own IP, no IPv6 leak
- clones (user 999) are detected in Diagnostics and routed
- The signing key is backed up. Changing it forces an uninstall.
Source code and license
BitProxy is free software under the GNU General Public License v3.0 (see LICENSE), as are
sing-box and sing-box-for-android, which it is built from. The complete source of each
release is the upstream code at the versions in versions.env plus the patches in
patches/. build-release.sh rebuilds it exactly. BitProxy isn't affiliated with or
endorsed by SagerNet / sing-box; "sing-box" names the upstream project it's based on.
Thanks to nekohasekai and the SagerNet contributors for sing-box and its Android app, a great piece of free software. If BitProxy is useful to you, consider supporting the upstream project.