Review findings #1-#3 in internal/APP_REVIEW_2026-10-16.md: every session end dropped the user onto their real IP with no warning, and the app showed no errors at all. - RevoltAlerts (vendored runtime) owns both alerts: a high-importance channel, ids 2 and 3, posted from whichever process is alive - XrayVPNService schedules a 5-minute warning off the deadline it already holds, best effort (a warning that cannot be armed never stops a tunnel), with its own request code so it cannot clobber the expiry alarm - the warning intent is handled above the command dispatch: without that it would fall through to the final else and stop the tunnel 5 minutes early - SessionTimer._doDisconnect takes endedMessage: set for ends the user did not ask for, which both posts the alert and gives the UI a sentence - the API being unreachable no longer disconnects a working tunnel; it is a display state now, with expiry still enforced natively and server-side - status_bar.dart renders connect errors, connecting progress and the session-end reason flutter analyze clean; debug APK builds (Kotlin compiles). Co-Authored-By: Claude Opus 5 <[email protected]>
510 lines
26 KiB
Markdown
510 lines
26 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to the ReVoltVPN Flutter app.
|
|
Versions follow [semver](https://semver.org/): MAJOR.MINOR.PATCH
|
|
|
|
---
|
|
|
|
## [Unreleased]
|
|
|
|
### Added
|
|
- **Five-minute warning before a session ends.** The VPN service schedules it
|
|
off the same deadline it already enforces, so the warning still arrives when
|
|
the app is closed, and a support top-up moves it with the deadline.
|
|
- **"Disconnected — not protected" notification** whenever a session ends for
|
|
any reason other than you tapping disconnect: expiry, the data limit, the
|
|
server ending the session, or the tunnel stopping on its own.
|
|
- **The app now shows what went wrong.** Connect errors, the reason a session
|
|
ended, and progress while connecting appear under the status bar instead of
|
|
the button silently going back to idle.
|
|
|
|
### Changed
|
|
- **A control-plane outage no longer tears down a working tunnel.** The app used
|
|
to disconnect itself after about two minutes of failed status polls, putting
|
|
you back on your real IP; it now keeps the tunnel up and shows
|
|
"Can't reach the server — still connected". Expiry is still enforced by the
|
|
VPN service's own deadline and by the server.
|
|
|
|
### Fixed
|
|
- **Rewarded ads no longer break connecting.** With ads enabled, the ad's
|
|
server-side-verification nonce and the nonce the connect flow waited for were
|
|
minted separately, so the client rejected the very session the ad created and
|
|
failed with "Session not activated". The ad's nonce is now passed through to
|
|
the connect call, the fake bypass callback is skipped when a real ad was
|
|
watched, and the client polls longer (~36 s) for Google's callback.
|
|
- **SOCKS5 "Reconnect" re-attaches to the current session** instead of
|
|
requesting a new one, so repairing a lost proxy no longer needs (or grants)
|
|
a fresh session.
|
|
|
|
---
|
|
|
|
## [3.3.6] — 2026-10-08
|
|
|
|
### Fixed
|
|
- **VPN service notification icon.** The ongoing notification now uses the
|
|
bundled `notification_icon` drawable instead of `notification_status_icon`,
|
|
and the vendored runtime resolves that drawable by name at runtime (falling
|
|
back to the platform icon only if it is missing), so the foreground-service
|
|
notification no longer shows the generic Android info icon.
|
|
- **"Test Local SOCKS" no longer claims an outbound it never tested.** Xray
|
|
answers a SOCKS `CONNECT` before it dials upstream, so trusting the `0x00`
|
|
reply reported success for unroutable addresses and for hostnames that can
|
|
never resolve — meaning the test said OK while the server was down, which is
|
|
exactly when it gets run. It now forces a request/response round trip through
|
|
the tunnel and reports distinctly when the outbound never replies, drops the
|
|
connection, or returns unexpected data.
|
|
|
|
---
|
|
|
|
## [3.3.5] — 2026-09-06
|
|
|
|
### Added
|
|
- **Reproducible release verification.** The release build pins the production
|
|
`app_config.dart` by SHA-256 and fails closed on any mismatch, so a release
|
|
APK cannot silently ship a template or wrong server config.
|
|
|
|
### Changed
|
|
- **Vendored Android runtime.** The pinned `flutter_vless` Android runtime is now
|
|
vendored in-tree instead of being patched in the pub cache at build time.
|
|
- **Harden the control-plane trust boundary.** `/session/status` fields are
|
|
strictly validated (nonce, destination IP, port, SNI, XHTTP path), control
|
|
responses are capped and never follow redirects, and the tunnel destination
|
|
is pinned to the compiled server — a compromised API can no longer redirect
|
|
user traffic.
|
|
- **Authenticated local SOCKS5 is now mandatory.** The ephemeral local proxy
|
|
replaces rather than filters inbounds, so only the authenticated SOCKS5
|
|
listener can exist.
|
|
- **Fail closed on connection-mode changes.** The mode is persisted before it is
|
|
applied, and a failed save leaves the UI and runtime unchanged instead of
|
|
silently selecting a weaker route.
|
|
|
|
### Fixed
|
|
- Connect button no longer leaks a listener into a disposed `AnimationController`
|
|
during teardown.
|
|
- Disclosure acknowledgement is only persisted once the user accepts it.
|
|
- Session countdown now uses a monotonic clock (cannot be extended by rolling the
|
|
device clock back) and a VPN error tears the tunnel down instead of freezing
|
|
the timer on a live session.
|
|
- The VPN-service notification's disconnect action and countdown re-post are
|
|
aligned with the vendored runtime.
|
|
|
|
### Removed
|
|
- Unsupported server-selector UI (single server today; design retained for
|
|
multi-server later).
|
|
- Unused Flutter dependencies and orphaned assets.
|
|
- Dead Dart wrappers whose native handlers no longer exist.
|
|
|
|
### Security & privacy
|
|
- IPv6 is captured by the VPN interface (`::/0` route), closing a leak where
|
|
IPv6 traffic could bypass the tunnel on IPv6-capable carriers.
|
|
|
|
---
|
|
|
|
## [3.3.4] — 2026-09-02
|
|
|
|
### Added
|
|
- **OS-level session-expiry enforcement.** The VPN service now holds an absolute
|
|
deadline and tears the tunnel down on its own clock — a main-looper handler
|
|
plus an `AlarmManager` exact alarm (falling back to an inexact alarm on
|
|
Android 13+ when exact alarms aren't granted). The app pushes
|
|
`now + expires_in_seconds` on each session sync, so a session ends even if the
|
|
UI process has been killed.
|
|
- **Session survival across process death.** The service persists the active
|
|
session and re-establishes it when Android restarts it (`START_STICKY`).
|
|
- **Support-ad extension reflected immediately.** A rewarded "Support us" ad now
|
|
forces an immediate server sync, so the countdown and expiry deadline update
|
|
without waiting for the next heartbeat.
|
|
|
|
### Changed
|
|
- **Session-status beacon slowed.** Foreground poll: 5 s → 60 s; background poll:
|
|
30 s → 60 s.
|
|
- **Background-reliability guidance.** The "Always-on VPN" settings deep-link was
|
|
removed; the battery-optimisation exemption remains, with expiry now enforced
|
|
by the service itself.
|
|
|
|
### Removed
|
|
- Legacy `ass` connection-mode migration — unrecognized persisted modes now
|
|
default to TUN.
|
|
|
|
### Security & privacy
|
|
- Session credentials and the per-user VLESS UUID are now held app-private on
|
|
disk (`active_session.bin`) for the session's lifetime and removed on
|
|
disconnect or expiry. Previously they were memory-only.
|
|
|
|
---
|
|
|
|
## [3.3.3] — 2026-09-01
|
|
|
|
### Fixed
|
|
- **The session clock stayed at 00:00:00 on a live tunnel, and the notification
|
|
countdown froze with it.** `SessionTimer` is built lazily, so when the app reattaches
|
|
to a tunnel that outlived its UI process, `VpnConnection` had already reported
|
|
"connected" before the timer existed — and a change listener does not replay events
|
|
it was not present for. The timer now evaluates the current connection state once at
|
|
construction instead of waiting for a transition, and is created eagerly alongside
|
|
`VpnConnection`. Because the timer is what pushes text to the foreground
|
|
notification, a stopped clock also meant a frozen countdown; both come from this.
|
|
- Resuming the clock no longer requires `adoptedRunningRuntime` or a previous
|
|
successful sync, both of which are false in a freshly started process. A connected
|
|
tunnel with a stopped clock is now always treated as something to resume.
|
|
|
|
## [3.3.2] — 2026-09-01
|
|
|
|
Background reliability. 3.3.1 could show "connected" against a tunnel that was no
|
|
longer running; this release fixes the cause rather than the symptom, and removes the
|
|
machinery that existed to work around it.
|
|
|
|
### Added
|
|
- **Background reliability settings.** Android stops background services of apps that
|
|
are not exempt from Doze, and tears down the process group when a task is swiped
|
|
away. Two OS mechanisms prevent that, and the app now surfaces both: a one-tap
|
|
**battery optimisation exemption**, and a deep link to Android's **Always-on VPN**
|
|
settings (with a note about "Block connections without VPN"). Neither can be enabled
|
|
programmatically — each needs a single user tap, once.
|
|
|
|
### Fixed
|
|
- **The app could report "connected" with no traffic flowing.** Startup state came from
|
|
`getConnectedServerDelay()`, which measures through
|
|
`AppConfigs.V2RAY_CONFIG?.LOCAL_SOCKS5_PORT ?: 10807`. `AppConfigs` lives in the VPN's
|
|
own process, so from the UI process it is always null and the port always fell back to
|
|
10807. That was accidentally correct while the SOCKS port was fixed; 3.3.1's
|
|
per-session ephemeral ports made it permanently wrong. Liveness is now determined from
|
|
the OS — whether our VPN service process is running, and whether a VPN transport is
|
|
actually present.
|
|
- **A frozen countdown could outlive the tunnel.** The foreground notification (id 1) is
|
|
owned by the VPN service via `startForeground`, but the UI process was posting to the
|
|
same id. When that process died, its notification stayed on screen with nothing left to
|
|
update it. The app no longer posts for a runtime that is not alive, and clears a stale
|
|
notification instead.
|
|
|
|
### Removed
|
|
- **Standard/Extreme resilience modes** and the runtime-restart machinery behind them.
|
|
Recovery ran off a disconnect broadcast delivered to the app process, so it could never
|
|
fire in the case that actually matters — Android killing that process. It was
|
|
complexity substituting for background persistence.
|
|
- **Startup restoration**, including the config-snapshot rebuild. With the tunnel kept
|
|
alive by the OS, there is nothing to restore; the app simply reports what is running.
|
|
- The `resilience_mode` preference and its settings tile.
|
|
|
|
## [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
|