The Android app is a Kotlin Multiplatform VPN app that runs in either client mode (routes the device's traffic through a peer) or server mode (auto-answers calls from peers and bridges their traffic to the open internet via an in-process userspace TCP/IP stack). No root, no kernel TUN, no iptables.
Persian / فارسی: راهنمای کاربری اپلیکیشن اندروید
- Two Android devices, both signed in to Bale.
- Each device must have the other in its Bale contact list — Bale only allows calls between mutual contacts.
- One device with a working internet connection (the server); one that wants to use it (the client).
Download the latest APK from the repository's Releases page and install it. Open the app, enter your phone number, confirm the SMS code, and sign in. Do this on both devices.
- Toggle the mode switch to Client.
- Tap Select Peer and pick the contact that will act as the server.
- Tap Start VPN. Android shows a system VPN-permission dialog — Allow.
- Done. All traffic from this device now flows through the server. Live throughput appears below the button.
To disconnect, press Disconnect.
- Toggle the mode switch to Server. The foreground service starts automatically and waits for incoming calls.
- (Optional) Contacts — add anyone else by phone number who should be able to connect to you.
When someone calls in for the first time you'll get a notification. Open the app and you'll see a pending row with two buttons:
- Allow — accepts the caller and adds them to the allow-list. Future calls from this ID connect without prompting.
- Reject — disconnects them and adds the caller ID to the block-list. Future calls from that ID are auto-rejected silently (no notification, no prompt). To undo this, open Manage Clients (described below) and press Unblock on their row.
The two lists are mutually exclusive — explicit Allow/Reject moves a caller between them. Pending requests auto-reject after 60 seconds (without blacklisting; the caller can retry later).
Rows are grouped (with divider lines) into connected, allowed offline, and blocked. Each row shows the caller's name, ID, an inline · Allowed / · Blocked membership tag, and — for connected clients — live throughput and totals.
Per-row actions:
- Disconnect — kicks the current session only. There's no auto-retry from the client app — the caller has to manually re-initiate from their side. If they're still on the allow-list, that new call is auto-accepted.
- Remove — drops them from the allow-list and kicks the active session. Future calls go to the pending notification (no auto-accept), but the caller isn't blocked.
- Limit — set per-direction kbps cap, 1–1000. Default is 500 kbps. Persists for admitted callers; session-only otherwise.
- Stats — opens a per-client detail screen with live RTT, retransmits / TLP fires / SACK losses, congestion-window averages, TCP flow-state breakdown, and incoming-queue depth.
- Unblock — only on blocked rows; removes the caller from the block-list.
Action-bar overflow:
- Max clients… — cap on simultaneously-connected callers (1–253, default 5). New calls above the cap are dropped without blacklisting.
- Debug logs ON/OFF — verbose TCP/IP diagnostics in logcat.
All settings (allow-list, block-list, per-caller caps, max-clients) persist in SharedPreferences("config") across app restarts and device reboots. Clear them via Settings → Apps → Storage → Clear data.
To stop the server, press the Disconnect button on the server screen. This kicks every active client and stops accepting new incoming calls. Press Connect to resume.
The Android client only routes its own traffic. To share the same internet with a laptop or another phone, install a HTTP/SOCKS proxy app on the client device (e.g., EveryProxy), turn the client device into a Wi-Fi hotspot (or join the same Wi-Fi), then point the other devices at the proxy address shown by EveryProxy.
- Bale only allows calls between mutual contacts — both phones must save the other's number.
- The app needs no root and no special privileges.
- In server mode, the device must stay on with the app in the foreground or background — the foreground service keeps it alive.
- Bale's servers see the connection as a long voice call. They can see your destinations and any unencrypted payload — see the privacy note in the main README.
In server mode the app implements its own TCP/IP stack inside the process and does NAT entirely in userspace — incoming IP packets from a peer are terminated, routed, and forwarded to the open internet without touching the kernel's networking stack. That's why server mode runs from a plain APK on any phone, with no root and no special privileges.
This was a deliberate design choice for user convenience — anyone can install and run the server. The trade-off is performance: a rooted device using the kernel's TCP/IP stack and a real TUN device would be measurably faster. If you have access to a Linux or macOS box, the Linux/macOS Node TUN VPN server is the high-throughput option (kernel-level NAT instead of userspace).





