Files
ReVoltVPN/CHANGELOG.md
T
esefxdzandClaude Opus 5 ac52681452 docs(changelog): rewrite 3.3.1 against the shipped tree
The notes described PR #2 as opened, not what is being merged. Removed the
entries for per-app routing, the installed-apps bridge and the app picker
-- all dropped in the debloat -- and moved the rationale to a Removed
section. Replaced the overstated validation claims with measured ones, and
documented the startup-restoration fixes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-31 23:31:51 +03:00

17 KiB

Changelog

All notable changes to the ReVoltVPN Flutter app. Versions follow semver: MAJOR.MINOR.PATCH


[3.3.1] — 2026-08-31

Added

  • Explicit TUN and SOCKS5 connection modes. The previous Auto mode is gone, so routing is chosen deliberately instead of inferred at runtime. TUN is the default. SOCKS5 runs a local authenticated proxy with no VPN interface, and therefore never requests the Android VPN permission.
  • Authenticated per-session Local SOCKS5. Every connection binds an ephemeral loopback port and mints fresh 16-byte username / 32-byte password credentials. Android loopback is reachable by every app on the device, so this authentication is load-bearing rather than decorative. Credentials stay in memory. Imported socks/http inbounds are dropped so no unauthenticated listener can survive alongside the session one.
  • Network resilience profiles — Standard and Extreme. Extreme restarts a failed Xray/tun2socks runtime up to twice before giving up.
  • Android physical-network monitoring, filtered on NET_CAPABILITY_NOT_VPN so the VPN's own network cannot feed back a reconnect loop. Informational only; the active transport (Wi-Fi / cellular / ethernet) now appears in the status bar.
  • Local SOCKS5 diagnostics. "Test Local SOCKS" authenticates against the exact active session over loopback — a readiness handshake plus a full CONNECT — instead of checking whether a fixed port accepts connections.
  • Native Android bridges for network state, haptic feedback and install-source detection.

Fixed

  • Extreme recovery now survives an app-process restart. Android can kill the Dart process while the VPN foreground service keeps running, leaving the app attached to a live tunnel it holds no configuration for — so recovery had nothing to restart from. In TUN mode the app rebuilds that snapshot from the still-active session, without opening a new session, showing an ad, or interrupting traffic.
  • The proxy settings tile no longer misreports state after a process restart. Its credentials genuinely cannot be recovered — they are random, generated on device and never transmitted — so instead of telling an already-connected user to "Connect", it now says the credentials were lost and offers a Reconnect action.
  • The first tap on the connect button was ignored. The debounce timestamp was initialized to the current time instead of the epoch, so the first press was always swallowed.
  • The health probe polled /health every 30 s even while connected. Restored to idle-only — a live tunnel already proves reachability, and the extra direct requests were unwanted.
  • A user reconnect could race an in-flight Extreme recovery. A connection generation counter now aborts stale recovery attempts.
  • Recoverable Xray/tun2socks startup and readiness failures no longer follow the unstable self-stop path used during development.
  • Returning from Android background state reconciles the session countdown against elapsed wall-clock time and re-syncs immediately, reducing stale countdowns and false Syncing… presentation after Dart throttling.
  • Sidebar update checks retain valid navigator/messenger handles after the drawer closes.

Changed

  • The pinned flutter_vless_android 1.1.5 runtime is patched at build time instead of vendored. Three idempotent patchers are chained into preBuild, so a plain flutter build apk behaves like CI rather than depending on a manual step against the pub cache. They are fail-closed: an upstream change breaks the build loudly instead of silently producing an unpatched runtime.
  • Legacy auto preferences migrate to TUN; legacy ass preferences migrate to SOCKS5.
  • Haptic feedback is a single confirmation on connect/disconnect, routed through the native Android vibration channel, and defaults to off.
  • The VPN notification uses a dedicated 24 dp status icon and stays ongoing/no-clear while the foreground service is alive.
  • Hivemind session parsing is split into typed validation steps; a malformed port now fails instead of silently falling back.
  • The update checker parses semver with prerelease ordering and validates release URLs against an HTTPS host allowlist.
  • The support button shows a claimed/disabled state once the session bonus is used.
  • The status bar distinguishes Ready from Syncing… and folds in server reachability.
  • Gradle release heap raised to 4 GiB with MaxMetaspaceSize=1g and workers.max=2, so Flutter/Jetifier transforms do not fail on the release classpath under constrained runners.
  • App version is now 3.3.1+27.

Removed

  • Per-app routing, including the app picker, the installed-apps native bridge and the blocked-apps mechanism. It only ever functioned in TUN — the underlying addDisallowedApplication call lives inside the VPN-interface setup that SOCKS5 mode skips entirely — and its allowlist half was never wired to any caller. Removed rather than finished.
  • The legacy auto connection mode.
  • flutter_local_notifications, and the unused notification manager it backed.
  • A global touch listener that fired a haptic on every tap.
  • Unused runtime telemetry fields.

Security & privacy

  • Session metadata is hidden from public lock-screen previews. The live foreground notification is built with VISIBILITY_PRIVATE plus a generic public version, so the countdown and speed are visible only after unlock. (An earlier attempt at this landed in an unreferenced class and never took effect.)
  • Local SOCKS5 listens only on 127.0.0.1, requires per-session authentication, and its credentials are neither persisted nor logged.
  • The app requests no package visibility at all. QUERY_ALL_PACKAGES was never used, and the launcher-enumeration query was removed along with the app picker.
  • Session nonces use Random.secure() and are never logged.
  • Device UUIDs are validated as v4 and regenerated if corrupt, so tampered identifiers cannot reach the API.
  • FOREGROUND_SERVICE_SPECIAL_USE is declared for Android 14+; the merged service declaration carries the vpn special-use subtype.
  • 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 — 0 errors, 0 warnings, 7 style infos (6 of them pre-existing).
  • All three build-time patchers verified against a clean pub cache, including that each applies independently of the others.
  • Not yet verified: Android release Kotlin compilation and APK build.

[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