# vojo proxy — agent onboarding Native Android proxy client. App traffic is captured by a `VpnService` tun, fed through a vendored native tun2socks engine into a **local** SOCKS5 server, then out to a real upstream proxy (SOCKS5 / HTTP(S) / Shadowsocks). UI is Jetpack Compose; persistence is DataStore. The repo-root [`README.md`](../README.md) is the product / Russian overview; this `docs/` set is the agent map. It exists so an agent session can load context fast. Read in this order: 1. **[architecture.md](architecture.md)** — the data path, the component map, and *why* the design is shaped this way (the local-SOCKS5-bridge, what runs native vs Kotlin). 2. **[building-testing.md](building-testing.md)** — how to build the APK and run the three test layers (JVM unit tests, the interop harness, on-device QA). 3. **[conventions.md](conventions.md)** — code style, commit rules, and the **load-bearing invariants** — choices that look wrong but are deliberate. Read this before "fixing" the data path; several obvious-looking changes have already been tried and reverted. ## 60-second orientation - **Language/stack:** Kotlin + Compose (Material3), coroutines, kotlinx-serialization, DataStore Preferences. Native C: a vendored `hev-socks5-tunnel` + a thin JNI bridge. - **Modules:** single Gradle module `:app`. `minSdk 28`, `compileSdk 35`, `arm64-v8a` only. - **Package root:** `chat.vojo.proxy` (debug app id `chat.vojo.proxy.debug`). - **Entry points:** `MainActivity` → `ui/MainScreen` (4 tabs: Туннель / Серверы / Приложения / Опции). `service/ProxyVpnService` is the `VpnService`. `service/ProxyTileService` is the quick-settings tile. - **Single source of truth for state:** `data/ConfigStore` (persisted `ProxyState`) and `service/VpnState` (live tunnel status/journal as `StateFlow`s). - **No upstream "universal core".** sing-box / xray / clash are GPLv3 and would force open-sourcing this closed app (and bloat the APK). The engine is `hev-socks5-tunnel` (MIT); the Shadowsocks crypto and proxy clients are our own Kotlin, verified against canon. See conventions.md before proposing a core swap. ## Where things live | Area | Path | |---|---| | VpnService lifecycle, tun builder, probe, reaper | `app/src/main/java/chat/vojo/proxy/service/ProxyVpnService.kt` | | Local loopback SOCKS5 server (hev talks to this) | `core/socks/LocalSocks5Server.kt` | | Upstream clients | `core/upstream/{Socks5,Http,Shadowsocks}Upstream.kt`, `Upstream.kt` | | Shadowsocks AEAD (SIP004) | `core/crypto/{ShadowsocksCrypto,ShadowsocksStream}.kt` | | SOCKS address codec / stream utils | `core/net/{SocksAddress,StreamUtils}.kt` | | Config model + share-link import | `core/ProxyConfig.kt`, `core/ConfigImport.kt` | | Persistence / app list | `data/{ConfigStore,AppRepository}.kt` | | UI | `ui/` (`MainScreen`, `ConnectionScreen`, `ServersScreen`, `AppsScreen`, `SettingsScreen`, `MainViewModel`) | | Native (JNI bridge + engine) | `core/Tun2Socks.kt`, `app/src/main/jni/tun2socks.c`, `app/src/main/jni/hev-socks5-tunnel/` | | Domain routing (matcher, geosite, direct dial) | `core/route/`, `core/upstream/DirectDialer.kt` | | UI language (RU/EN, no AppCompat) | `core/LocaleManager.kt`, `res/values{,-ru}/strings.xml` | | Dev tools (geosite refresh, device config injector) | `tools/fetch-geosite.sh`, `tools/mk_datastore.py` | | Unit tests | `app/src/test/` |