# Changelog All notable changes to the ReVoltVPN Flutter app. Versions follow [semver](https://semver.org/): MAJOR.MINOR.PATCH --- ## [3.3.1] — 2026-08-31 ### Added - **Explicit TUN and SOCKS5 connection modes.** The previous Auto mode is removed so routing behavior is selected deliberately instead of inferred at runtime. - **Per-app routing** with All apps, Exclude apps and Selected only policies. Android enumerates launcher applications without requesting `QUERY_ALL_PACKAGES`. - **Authenticated per-session Local SOCKS5.** Every connection creates a loopback-only SOCKS ingress with a random high port plus fresh username/password credentials. Credentials stay in memory and the diagnostic test uses the active session instead of fixed `10807`/`10808` ports. - **Network resilience controls** with Standard and Extreme profiles plus Android physical-network monitoring that ignores VPN-created network callback loops. - Native Android bridges for installed-app discovery, network state and haptic feedback. - Regression tests for legacy Auto/ASS migrations, secure SOCKS session generation and the patched stock Xray SOCKS authentication schema. ### Changed - Legacy `auto` preferences migrate to TUN. Legacy `ass` preferences migrate to SOCKS5 while preserving selected-app package lists. - Keep the hosted `flutter_vless_android` **1.1.5** runtime instead of restoring the experimental vendored Android service. Required routing, recovery and authenticated SOCKS behavior is applied by idempotent build-time patchers. - Session countdown reconciliation now uses elapsed wall-clock time, re-syncs immediately on resume and preserves the existing 5 s foreground / 30 s background poll policy. - Haptic feedback is initialized at startup and routed through a native Android channel; duplicate toggle feedback is removed and the setting remains persistent. - VPN notification uses a dedicated 24 dp status icon, remains ongoing/no-clear while the foreground service is active and keeps session metadata out of public lock-screen previews. - Hivemind session parsing is split into typed validation steps and nonce generation uses `Random.secure()` without logging nonce values. - Increase Gradle release-build heap to 4 GiB and cap workers at two so Flutter/Jetifier transforms do not fail on the release classpath under constrained CI runners. - App version is now `3.3.1+27`. ### Fixed - Recoverable Xray/tun2socks startup and readiness failures no longer follow the unstable vendored-runtime self-stop path used during development. - SOCKS5 readiness checks authenticate against the exact active session instead of merely checking whether a fixed local port accepts connections. - Returning from Android background state reconciles the session timer and server status, reducing stale countdowns and false `Syncing…` presentation after Dart throttling. - Sidebar update checks retain valid navigator/messenger handles after the drawer closes. - Settings startup uses the persistent haptic manager instead of the removed legacy loader. ### Security & privacy - Local SOCKS5 listens only on `127.0.0.1`, requires per-session authentication and does not persist or log generated credentials. - The app picker relies on launcher-app queries rather than broad package visibility. - Session time/speed notification metadata is marked private for lock-screen previews. - Existing AdMob bypass/callback behavior is intentionally unchanged by this release. - Server, nginx and Reality configuration are not modified by this client-side release. ### Validation - `flutter analyze` completed without fatal warnings or infos. - All four Flutter regression tests passed. - Android release Kotlin compilation passed. - The 3.3.1 release APK built successfully and the packaged VPN runtime verification passed. ## [3.3.0] — 2026-08-30 ### Added - Session countdown in the VPN notification, updated each second while connected. ### Fixed - **Session-expiry desync** — the app could show "Online" with no internet after the server ended the session. The countdown no longer freezes when a poll fails. - **Notification Disconnect button** disappeared a second after connecting. Re-posting the foreground notification replaces it wholesale, so the action, colour and tap-intent flutter_vless sets are now rebuilt rather than dropped. - **Speed readout** divided the byte delta by the nominal poll interval instead of the real time between polls — wrong by up to 6x around every background switch. - **Settings toggles** now apply at launch. Rain, lightning and haptics were only read from disk when Settings mounted, so an effect turned off came back — ticker and all — on every relaunch, and haptic feedback did nothing until Settings had been opened at least once. - **Haptic feedback** now fires `mediumImpact()` instead of `lightImpact()` (which is imperceptible on many Android devices), wrapped best-effort so a platform vibration failure cannot fail a connect/disconnect, with a preview tap when the toggle is switched on. - First-launch disclosure dialog said data was linked to "your account". It is linked to a randomly generated device ID, and the policy is in the sidebar, not on a website. - Sidebar "Settings" and "Check for updates" used the drawer's context after `Navigator.pop()` — a defunct context. They now capture the root navigator (and messenger) before closing the drawer. *(Fix ported from Naveax's review.)* ### Performance - Rain overlay ticks only while enabled, repaints through a scoped `Listenable` instead of rebuilding every frame, and precomputes drop colours. - Lightning no longer fires strikes while disabled. - Connect button pulse pauses when backgrounded; brand image hoisted out of the per-frame rebuild. - Server poll drops to 30 s while backgrounded (5 s foreground), with an immediate re-sync on resume. The 1 s countdown keeps running. - Health probe runs only while disconnected, and pauses in the background. - Speed and support widgets rebuild on value change instead of every second. - Notification channel is created once per process, not once per second. ### Changed - VLESS engine initializes under the splash screen; the intro waits on `VpnConnection.ready` (8 s cap) before showing the main screen. - AdMob SDK init moved off the blocking pre-`runApp()` path so startup cannot hang on an unreachable Google endpoint. - `app_config.example.dart` drops the dead bootstrap fields and documents `hivemindApiPublic` in their place. - `.gitignore` reorganized around type and directory rules; the enumerated filename list is gone (redundant with the type rules) and `.claude/` is now ignored. ### Security - Update checker pins the release URL to this repo's path on `github.com`, not just the host. Anything else falls back to the releases page. - Notification timer updates are gated on a live tunnel, so a late tick cannot leave an ongoing notification with no foreground service behind it. - Dropped `READ_EXTERNAL_STORAGE`, which `flutter_vless` pulls in but the app never uses (least privilege). - Pinned the Gradle 8.14 distribution SHA-256; `validateDistributionUrl` enabled (supply-chain integrity). ### Build & tooling (ported from Naveax) - Removed the deprecated `android.enableR8` flag. - Added `analysis_options.yaml` (flutter_lints, `local_packages/**` excluded, `avoid_print`). - Added `.gitattributes` to pin LF line endings repo-wide. - Credit: reviewed and ported from **Naveax**. ### Docs - Privacy policy: disclosed that the server sees the connecting IP, corrected the "stored in memory" claim, and documented consent being changeable from the sidebar. ## [3.2.2] — 2026-08-08 ### Fixed - Updated Discord invite link. ## [3.2.1] — 2026-08-08 ### Google Play compliance - Added first-launch data disclosure dialog ("Before you connect") explaining that connection duration and data usage are logged for quota enforcement, and that traffic content is never monitored. ## [3.2.0] — 2026-08-08 ### DPI hardening - Spoof Chrome-on-Android User-Agent on all API calls instead of `Dart/3.x`. - Renamed domain from `getrevolt.app` to `userevolt.app`. ### Branding - App name changed from "REVOLT VPN" to "Revolt VPN" across manifest, splash, notification, and VLESS URL remark. ### Notification - Removed duplicate `flutter_local_notifications` notification. Only the Android VPN foreground-service notification remains — non-swipeable. - Patched `flutter_vless` native code to use custom notification icon (`@drawable/notification_icon`) with dark background (`#0D1117`). ### Connection reliability - Merged five separate disconnect paths into a single idempotent `_doDisconnect()` gate. Eliminated race between timer ticks and VPN teardown where the countdown could briefly show -1. - `stopVless()` timeout (5 s) now correctly shows an error instead of silently pretending the VPN disconnected while the tunnel was still alive. ### AdMob bypass - `kDebugMode` guard removed — the debug AdMob callback now fires in release builds so release APKs can create sessions without real ads during testing. The real gate is `ADMOB_BYPASS` on the server. ### Fixed - **Notification icon:** Replaced five density-specific `ic_launcher.png` files with `notification_icon.png`, then reverted to avoid changing the home-screen icon. The custom icon is now injected through flutter_vless's `initializeVless()` parameters. ## [3.1.0] — 2026-08-06 ### Architecture — domain-free client - **Zero domain strings in APK.** RKN has nothing to DNS-block. API calls go through the Reality tunnel to `10.254.254.1:5000`, redirected to Flask by Xray's `api` freedom outbound (`redirect: 127.0.0.1:5000`). - **Bootstrap tunnel.** Bundled Reality config in APK → connect → fetch real per-session VLESS URL from inside the tunnel → reconnect. Bootstrap client permanently in `xray_config_reality.json`, restricted by Xray routing to internal API only. - `serverDomain` removed from `AppConfig`. `fetchConfigThroughTunnel()` replaces `fetchConfigWithPolling()`. `connect()` rewritten. ### Protocol — TCP to XHTTP - Transport: TCP → XHTTP (H2 stream-up with Reality). No more XTLS-Vision, no `flow` field. Path-hidden behind `/revolt`. ### Fixed - **RenderFlex overflow** — status bar text wrapped in `Flexible` with `TextOverflow.ellipsis` to prevent overflow on narrow screens (≤360dp). - **Circular import** — removed `import 'package:revoltvpn/main.dart'` from `sidebar_drawer.dart`. `SidebarDrawer` converted to `StatefulWidget`; version footer loaded via `PackageInfo.fromPlatform()`. - Client hot-reload was silently broken — `xray api adi` no-op. Fixed: `adu`/`rmu` commands per Xray HandlerService spec. - Missing XHTTP `path` param in VLESS URL — now reads `xhttp_path` from `/session/status`. - `providerBundleIdentifier` was `com.revoltvpn.app` — must match `com.paladinvpn.app` for VPN service registration. - Reality keypair generated (was placeholder). Throttle `uplinkOnly`/`downlinkOnly` corrected (seconds, not bytes/sec). Fallback rate limits removed (fingerprint). ### Added - **Settings screen** — reached from a new sidebar row (between Website and Check for updates). Slide-from-right navigation via `PageRouteBuilder`. Folder: `lib/screens/settings/` with one file per feature in `in_settings/`. - **Haptic feedback toggle** — on/off switch in Settings for `lightImpact()` vibration on successful connect/disconnect. Default: off. Uses sync-bool pattern (no await on tap, no SharedPreferences in connect button). - **Rain/Lightning effect toggles** — two switches in Settings to control the animated background effects. Default: on. Effects detect the toggle within one frame (no Provider/notifier needed). ### Changed - **Timer box spinner removed** — the "Connecting…"/"Syncing…" spinner under the countdown was redundant with the connect button's spinner. Timer card is now a clean countdown only (gray '00:00:00' when idle). - **Sidebar** now has 7 rows (added Settings). `FRONTEND_POLISH_REVIEW.md` fully triaged: 4 items fixed, 11 skipped. ### Server - `api` outbound + 3 routing rules in `xray_config_reality.json` (bootstrap isolation + API routing). Permanent bootstrap client. Hivemind unchanged. - `xhttp_path` in `/session/status` response. - Full deployment guide: `SERVER_DEPLOYMENT.md`. --- ## [2.0.7] — 2026-07-31 ### Fixed - **Notification bug** — `SessionTimer.stop()` now cancels the persistent Android notification on user-initiated disconnect. - **Concurrent sync guard** — `_syncInProgress` flag prevents duplicate HTTP requests from overlapping in `_syncWithHivemind()`. - **Debug bypass gate** — `AppConfig.enableAdBypass` replaces `kDebugMode` for fake AdMob callback. Tree-shaken in release builds. ### Changed - **Responsive layout** — main screen uses `LayoutBuilder` with proportional fractions. Scales to any screen size. - **Dynamic version** — sidebar footer reads from `PackageInfo` instead of hardcoded string. - **Swarm/log blocked** — nginx returns 404 for `/api/swarm` and `/api/log`. Monitoring runs locally via `swarm_watch.py`. ### Added - **Update checker** in sidebar — polls GitHub Releases API. - **Website button** in sidebar → opens `https://userevolt.app`. - **Timer syncing indicator** — spinner during handshake. - Dependency: `package_info_plus`. ### Server - `/session/stop` fetches stats BEFORE popping session (race fix). - `XrayManager._stats_lock` for diagnostic counter thread safety. - `ManagementLoop._start_lock` prevents TOCTOU on daemon startup. --- ## [1.0.6] — 2026-06 - Initial VLESS + Xray Reality tunnel - 60 min session, 4 GB throttle, support top-up - Server health dot, speed display, countdown timer - Persistent Android notification - Glass-morphism dark UI with yellow accent - Right-edge sidebar with Ad Consent, Privacy, About, Discord