Quick SSH tunnels, in your pocket.
Mobile port of tunly — manage named SSH dynamic (SOCKS5) tunnels and route all device traffic through the one you pick. Android first; iOS planned (the core is already portable).
A mole digs tunnels — so does this.
🖥️ On Linux? The original tunly is a GNOME tray applet that drives the system proxy.
- Named tunnels — save several SSH endpoints, start one with a tap. One active at a time.
- Whole-device VPN — an Android
VpnServicecaptures all traffic and forwards it through the SSH tunnel via a userspace netstack (tun2socks). No root. - Reconnect + fail-closed — survives network switches (Wi-Fi ↔ cellular). While reconnecting, traffic is blocked rather than leaked; a watchdog restores normal networking if the tunnel can't recover, so you're never stranded offline.
- Key or password auth — imported private key (passphrase-capable) or password.
Tunly is a client. It doesn't provide servers — it tunnels your traffic
through an SSH server you control and then out to the internet. So your exit
IP becomes that server's IP. You need a Linux box you can SSH into with outbound
internet; almost any small VPS works (the tunnel only needs sshd + bandwidth —
no special software on the server).
Create a small Linux VM, note its public IP, and you're the root/sudo user.
Popular options (prices/tiers change — verify before signing up):
| Provider | Cheapest | Free tier | Notes |
|---|---|---|---|
| Google Cloud | — | 1× e2-micro/month free in select US regions |
Free-tier VM is enough for a tunnel |
| AWS | Lightsail ~$3.50/mo | EC2 t3.micro free for 12 months |
Lightsail is the simplest AWS path |
| Hetzner Cloud | ~€4/mo (CX22) | — | Cheapest reliable paid VPS; EU + US regions |
| Vultr | ~$2.50–5/mo | — | Many regions, hourly billing |
| DigitalOcean | $4/mo droplet | Signup credit (often $200/60 days) | Beginner-friendly |
| Linode (Akamai) | $5/mo | Signup credit | Simple, well-documented |
| Fly.io | small VMs, usage-based | limited free allowance | Container-style, quick to spin up |
- Spin up the smallest Linux VM (Ubuntu/Debian is fine).
- Add your public key to the server:
ssh-copy-id user@SERVER_IP(or paste it into~/.ssh/authorized_keys). - In Tunly: New tunnel → host =
SERVER_IP, user = your login, Key = paste the matching private key (stored encrypted; never shown again). - Tap ▶ and allow the VPN prompt.
Server hardening (recommended): use key auth and disable SSH passwords
(PasswordAuthentication no in /etc/ssh/sshd_config), keep the box patched,
and restrict the firewall to port 22.
- Secrets at rest — tunnel configs, including the private key/password, are
stored in Android Keystore-backed
EncryptedSharedPreferences(AES-GCM), never in plaintext.allowBackupis off, so they don't leave the device. - Host keys — trust-on-first-use: the server's key is pinned on first connect; a later mismatch is treated as a possible MITM and terminates the tunnel (no silent re-pin).
- No self-capture — the app excludes itself from its own VPN so the SSH uplink bypasses the tun (a full tunnel would otherwise trap its own bootstrap).
- The core never touches disk — secrets are passed in at connect time; the Go core reads no files and persists nothing.
Three layers; the Go core is the only shared/portable code.
Flutter (Dart) UI, tunnel list, encrypted config store
└ MethodChannel/EventChannel
Native shell (Kotlin) VpnService, tun fd, notification, reconnect watchdog,
└ gomobile .aar network-change nudge, Keystore secret store
Go core (.aar) SSH client + keepalive, tun2socks, DNS-over-TCP,
host-key TOFU, reconnect supervisor, state machine
core/— Go module (go test ./...runs on desktop; no mobile toolchain needed). Seecore/cmd/tunlye2efor a real end-to-end harness.app/— Flutter app + KotlinVpnServiceshell.docs/superpowers/— design spec and implementation plan.
Requires Go 1.25, the Flutter SDK, the Android SDK + NDK, and gomobile.
# 1. Build the Go core into an Android .aar
cd core
gomobile bind -target=android/arm64 -androidapi 23 \
-o ../app/android/app/libs/tunlycore.aar ./mobile
# 2. Build + install the app
cd ../app
flutter build apk --debug
flutter installThe .aar is a generated build artifact and is not committed.
Android: working end-to-end (SSH auth, host-key pinning, whole-device routing,
reconnect). iOS: the Go core is portable via gomobile's xcframework target; the
PacketTunnel shell is not built yet.
MIT © Ahmed Toulan