← Back to projects
Client workClient work · In device testing & handover

A commercial Android VPN built for mainland China, giving users reliable, secure access to services blocked by the Great Firewall. I built the whole thing solo: backend infrastructure, a React Native client with a native Android tunneling module doing real packet-level VPN, and a web admin panel. The product is client-owned; core build is complete and it's in testing and production handover.

React NativeKotlinsing-box / libboxAndroid VpnServiceNode.jsExpressTypeScriptPostgreSQLPrisma

Android VPN Platform


The brief

A client needed a commercial VPN product for mainland China, one of the hardest network environments on the planet to build for. The Great Firewall doesn't just block endpoints; it actively fingerprints and disrupts circumvention traffic in real time. The goal: give users inside China a reliable, secure way to reach services and content that are otherwise unreachable, on Android, without the connection collapsing the moment the network detects and pushes back on it.

This is client work. The product belongs to the client. I designed and built the technical solution end-to-end.


My role

Sole developer. Solo, freelance, full-stack across backend, mobile client, native tunneling module, and admin dashboard, all mine. There was no team to hand the hard parts to. I owned the architecture, the Android networking layer down to the TUN device, the API design, the server-side service logic, and the operational tooling the client uses to run it. The whole thing lives in a single pnpm-workspace monorepo (/mobile, /backend, /admin, /website, /shared).


Key decisions

Real VPN tunneling, not a wrapper. The Android client does actual low-level tunneling. It stands up a TUN device via Android's VpnService API and routes packets through sing-box's core, rather than wrapping an existing client or hiding a webview behind a logo. I built a native Kotlin module (MagicVpnService, VpnPlatformInterface) bridging React Native's UI layer to a libbox core compiled from sing-box via gomobile (SagerNet's fork, Go 1.23.4). Packet-level control was non-negotiable: against the GFW, you need to own the tunnel to have any chance of staying connected.

Four protocols, all for evasion. The server runs sing-box configured with WireGuard, VLESS+REALITY, Hysteria2, and Trojan+JLS. REALITY and JLS specifically exist to defeat active-probing and TLS fingerprinting, the GFW's signature moves, by making circumvention traffic look like ordinary TLS to real sites. Multiple protocols mean the service can fall back when one path gets characterized and throttled.

Working around the firewall inside the phone, too. sing-box's default interface monitoring uses a netlink socket, which is blocked in the Android app sandbox. I replaced that path with Android's ConnectivityManager so the tunnel could actually track network changes on-device. It's the kind of platform-specific problem that only shows up once you're doing real tunneling instead of shelling out to someone else's client.

A web admin panel so the client can run it without me. Operators manage users, servers, and service configuration from a Next.js admin dashboard, secured with JWT sessions. A VPN service is an operational product where servers change, users churn, and config gets tuned, so the people running it needed direct control without a developer in the loop for routine work.


Stack

  • Mobile client: React Native (New Architecture) + TypeScript, styled with NativeWind (Tailwind). Device token and settings storage via SharedPreferences.
  • Native tunneling module: Kotlin (VpnService, MagicVpnService, VpnPlatformInterface) bridging to libbox, sing-box's Android library, compiled via gomobile (SagerNet fork, Go 1.23.4). TUN device + foreground service; ConnectivityManager for interface monitoring.
  • Protocols: WireGuard, VLESS+REALITY, Hysteria2, Trojan+JLS, all via the sing-box core.
  • Backend: Node.js + Express + TypeScript. PostgreSQL with Prisma ORM. JWT for device tokens and admin auth; bcrypt for password hashing.
  • Admin panel: Next.js (App Router) + TypeScript + Tailwind CSS, JWT-secured admin sessions.
  • Marketing site: Next.js (static export) + Tailwind, deployed on Vercel.
  • Infrastructure: Oracle Cloud free-tier ARM VM (Ubuntu 24.04) running sing-box server-side with all four protocols, dual-stack IPv4 + IPv6.
  • Monorepo: pnpm workspaces (/mobile, /backend, /admin, /website, /shared).

Challenges & solutions

Low-level networking on Android. Real tunneling means working close to the metal: establishing the TUN interface, wiring it to the sing-box core, and managing the connection lifecycle correctly inside Android's foreground-service and sandbox constraints. This is the part most "VPN apps" avoid by wrapping something else. Doing it natively, through a Kotlin-to-libbox bridge, was the point, and it's where most of the hard engineering went.

Building against the Great Firewall. In China, "it connects on my machine" means nothing. The product was designed assuming the network is actively fingerprinting and probing it, which drove the protocol choices (REALITY and JLS to blend circumvention traffic into legitimate TLS) and the multi-protocol fallback strategy. The netlink-socket workaround via ConnectivityManager was another instance of the same theme: the environment blocks the obvious path, so you find another.

Owning the full stack, solo. A React Native client, a native Kotlin tunneling module, a Go-compiled core, an Express/Postgres backend, and a Next.js admin panel are five different disciplines. Keeping them coherent as one system, in a single monorepo, with one person holding all of it in their head, was its own challenge, and exactly the end-to-end ownership this project required.


Status

Core product is complete. Backend, React Native client, native Android tunneling module, and web admin panel are all built and functional, with a live test server on Oracle Cloud running all four protocols. The project is currently in device testing and production handover: validating the client across real devices and network conditions, and transitioning it to the client to operate. Near-complete and moving toward launch, not early-stage.