Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ go 1.25.4

replace github.com/sagernet/sing => github.com/getlantern/sing v0.7.18-lantern

replace github.com/sagernet/sing-box => github.com/getlantern/sing-box-minimal v1.12.21-lantern
replace github.com/sagernet/sing-box => github.com/getlantern/sing-box-minimal v1.12.19-lantern

replace github.com/sagernet/wireguard-go => github.com/getlantern/wireguard-go v0.0.1-beta.7.0.20251208214020-d78e69f1eff4

Expand All @@ -25,7 +25,7 @@ require (
github.com/alecthomas/assert/v2 v2.3.0
github.com/getlantern/common v1.2.1-0.20260326210434-cb69537aaf46
github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9
github.com/getlantern/radiance v0.0.0-20260415161948-4241e6c5a9c6
github.com/getlantern/radiance v0.0.0-20260417101633-2d396075314e
github.com/sagernet/sing-box v1.12.22
golang.org/x/mobile v0.0.0-20250711185624-d5bb5ecc55c0
golang.org/x/sys v0.41.0
Expand Down
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ github.com/getlantern/keepcurrent v0.0.0-20260304213122-017d542145ae h1:NMq3K7h3
github.com/getlantern/keepcurrent v0.0.0-20260304213122-017d542145ae/go.mod h1:ag5g9aWUw2FJcX5RVRpJ9EBQBy5yJuy2WXDouIn/m4w=
github.com/getlantern/kindling v0.0.0-20260329144042-b1825b9cb1bb h1:A92dC/E/HvkEb1r4tAwCFNlcMsGdqKe5GMmxeUFid9M=
github.com/getlantern/kindling v0.0.0-20260329144042-b1825b9cb1bb/go.mod h1:c5cFjpNrqX8wQ0PUE2blHrO7knAlRCVx3j1/G6zaVlY=
github.com/getlantern/lantern-box v0.0.65 h1:/PvWqm61PvSjlYWTpLhUHwacpLQ6gTQGXB6BCrYAdko=
github.com/getlantern/lantern-box v0.0.65/go.mod h1:n5NzI/rqr1USYIQPnEy3oZBYNPDyi8EODXNg8jPsQqY=
github.com/getlantern/lantern-box v0.0.67 h1:0uDILTY2fVzy47IoEecsMoeplqdxFU/KE/izaZXwM/Q=
github.com/getlantern/lantern-box v0.0.67/go.mod h1:n5NzI/rqr1USYIQPnEy3oZBYNPDyi8EODXNg8jPsQqY=
github.com/getlantern/lantern-server-provisioner v0.0.0-20251031121934-8ea031fccfa9 h1:6seyD2f9tz2am0YQd/Qn+q7LFiiQgnmxgwWFnVceGZw=
Expand All @@ -261,16 +263,18 @@ github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YL
github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q=
github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b h1:gMYJzEhLrmIqQ+JnjiYNm+UyUDalK3WUmVyecFwmV5g=
github.com/getlantern/publicip v0.0.0-20260328175246-2c460fe80c6b/go.mod h1:NpfXdK4ldEKkjQ4P1R+DBF4ua5VFOlxmgHROTnYrApg=
github.com/getlantern/radiance v0.0.0-20260415161948-4241e6c5a9c6 h1:M90CSsT1Ky7lXgHF+BPl0rqdI57viNjHzOAdpjEwqLI=
github.com/getlantern/radiance v0.0.0-20260415161948-4241e6c5a9c6/go.mod h1:FfYQpX2Hwk+TcIUVola4bgF7QIyPSmNsHrneDMQ2vPE=
github.com/getlantern/radiance v0.0.0-20260414121753-1a2d88b13b6e h1:AccPmAe69O8p2mVM+UIkgWT2lhxeKbQ/6PPMWX0Ej68=
github.com/getlantern/radiance v0.0.0-20260414121753-1a2d88b13b6e/go.mod h1:H7/Tvde1mBz1/lyz76VfiEdOGAWualquyJAl/LH7rbo=
github.com/getlantern/radiance v0.0.0-20260417101633-2d396075314e h1:zxW4xfPSeHtfByibiF1Us2EfGOlpzMIpIPanQ6zD4i0=
github.com/getlantern/radiance v0.0.0-20260417101633-2d396075314e/go.mod h1:pQoVlqah4QNAb5tPVu+vZ1LA1nB1JclXPjMWwFv+Ec0=
github.com/getlantern/samizdat v0.0.3-0.20260327203406-ef7323341974 h1:k+/qNo5YNO+8M8LVUp6G5Evm1OQdEs3Z4ye8top4AhI=
github.com/getlantern/samizdat v0.0.3-0.20260327203406-ef7323341974/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0=
github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb h1:c5YM7b3a4r2J8Eh89KkI6M/iTFe6Bi+b8AJlfkKdFq4=
github.com/getlantern/semconv v0.0.0-20260327040646-21845dda05cb/go.mod h1:GkPT5P9JoOTIRXRmFWxYgu1hhXgTFFTNc2hoG7WQc3g=
github.com/getlantern/sing v0.7.18-lantern h1:QKGgIUA3LwmKYP/7JlQTRkxj9jnP4cX2Q/B+nd8XEjo=
github.com/getlantern/sing v0.7.18-lantern/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/getlantern/sing-box-minimal v1.12.21-lantern h1:DUlwWDHrU60hd/83mvU/fR9aASiq4KaN5Z1wa8gaRtM=
github.com/getlantern/sing-box-minimal v1.12.21-lantern/go.mod h1:LzlFRel9E92gX0HXWCdsxgeg+kuAEPzLR+Znixk9EI4=
github.com/getlantern/sing-box-minimal v1.12.19-lantern h1:Tntq+Udsvyv6A/mjxfSoZ8NhvhXRSX6i/CICKGPFhAY=
github.com/getlantern/sing-box-minimal v1.12.19-lantern/go.mod h1:LzlFRel9E92gX0HXWCdsxgeg+kuAEPzLR+Znixk9EI4=
github.com/getlantern/timezone v0.0.0-20210901200113-3f9de9d360c9 h1:VTNjZxSuAHUzu13lYpEVB8gc3xz5hZePGNHG5enHYLY=
github.com/getlantern/timezone v0.0.0-20210901200113-3f9de9d360c9/go.mod h1:7uvbzuoOr3uYGHZx5QWlI8/C52XEf/aTb/tJFEe41Ak=
github.com/getlantern/waitforserver v1.0.1 h1:xBjqJ3GgEk9JMWnDgRSiNHXINi6Lv2tGNjJR0hCkHFY=
Expand Down
28 changes: 22 additions & 6 deletions lantern-core/vpn_tunnel/vpn_tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,33 @@ const (
var ipcServer atomic.Pointer[ipc.Server]

// StartVPN will start the VPN tunnel using the provided platform interface.
// It passes the empty string so it will connect to best server available.
// If the user previously selected a specific server (persisted by
// vpn.SelectServer), the new tunnel is pinned to that selection. Otherwise it
// falls back to AutoConnect which picks the best server.
//
// This is critical on Android: the OS VPN service lifecycle tears down and
// recreates libbox on every settings-driven restart, and the in-memory selector
// state of the previous libbox doesn't carry over. Reading the persisted
// selection here is what keeps the user pinned to their chosen server across
// routing-mode toggles, ad-block toggles, etc.
func StartVPN(platform rvpn.PlatformInterface, opts *utils.Opts) error {
// As soon user connects to VPN, we start listening for auto location changes.
slog.Info("StartVPN called")
if err := initIPC(opts, platform); err != nil {
return fmt.Errorf("failed to initialize IPC server: %w", err)
}
// it should use InternalTagLantern so it will connect to best lantern server by default.
// if you want to connect to user server, use ConnectToServer with InternalTagUser
err := vpn.AutoConnect("")
if err != nil {
if group, tag := vpn.LastSelectedServer(); group != "" && tag != "" {
slog.Info("Restoring persisted server selection", "group", group, "tag", tag)
if err := vpn.Connect(group, tag); err != nil {
slog.Warn("Failed to restore persisted server, falling back to AutoConnect",
"group", group, "tag", tag, "error", err)
vpn.ClearLastSelectedServer()
} else {
return nil
}
} else {
slog.Info("No persisted server selection found, falling back to AutoConnect")
}
if err := vpn.AutoConnect(""); err != nil {
return fmt.Errorf("failed to start VPN: %w", err)
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions lib/features/home/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class _HomeState extends ConsumerState<Home> {
VpnStatus(),
DividerSpace(),
LocationSetting(),
if (!PlatformUtils.isIOS) ...{
if (!PlatformUtils.isIOS) ...[
DividerSpace(),
SettingTile(
label: 'routing_mode'.i18n,
Expand All @@ -223,7 +223,7 @@ class _HomeState extends ConsumerState<Home> {
],
onTap: () => onSettingTileTap(_SettingTileType.smartRouting),
),
},
],
if (PlatformUtils.isAndroid ||
PlatformUtils.isMacOS ||
PlatformUtils.isWindows) ...{
Expand Down
18 changes: 14 additions & 4 deletions lib/features/home/provider/app_event_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ class AppEventNotifier extends _$AppEventNotifier {
Future<void> build() async {
watchAppEvents();
ref.onDispose(() {
appLogger
.debug('Disposing AppEventNotifier and cancelling subscriptions.');
appLogger.debug(
'Disposing AppEventNotifier and cancelling subscriptions.',
);
_appEventSub?.cancel();
});
}
Expand All @@ -38,8 +39,9 @@ class AppEventNotifier extends _$AppEventNotifier {
/// Currently, it listens for 'config' and server-location events.
void watchAppEvents() {
appLogger.debug('Setting up app event listener...');
_appEventSub =
ref.read(lanternServiceProvider).watchAppEvents().listen((event) {
_appEventSub = ref.read(lanternServiceProvider).watchAppEvents().listen((
event,
) {
final eventType = event.eventType;
appLogger.debug('Received app event of type: $eventType');
switch (eventType) {
Expand All @@ -52,6 +54,14 @@ class AppEventNotifier extends _$AppEventNotifier {
ref.read(homeProvider.notifier).fetchUserDataIfNeeded();
break;
case 'server-location':
// Only consume this event when the user is actually in auto mode.
// Otherwise (custom server selected) ignore it — applying it would
// silently flip the user's selection back to Smart Location on
// routing-mode changes or any other tunnel rebuild.
final currentLocation = ref.read(serverLocationProvider);
if (currentLocation.serverType != ServerLocationType.auto.name) {
break;
}
try {
final autoLocation = Server.fromJson(jsonDecode(event.message));
final countryName = autoLocation.location!.country;
Expand Down
11 changes: 11 additions & 0 deletions lib/features/vpn/provider/server_location_notifier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ class ServerLocationNotifier extends _$ServerLocationNotifier {
}
}

/// Flips the active selection to auto and clears any stale custom-server
/// identity fields so downstream UI does not keep highlighting a previous
/// manual selection. The existing [autoLocation] metadata is preserved so
/// the Smart Location label remains available until the next push event.
Future<void> switchToAuto() async {
if (state.serverType == ServerLocationType.auto.name) return;
final updated = state.copyWith(serverType: ServerLocationType.auto.name);
state = updated;
await _storage.saveServerLocation(updated);
}

Future<void> ifNeededGetAutoServerLocation() async {
final status = ref.read(vpnProvider);
final current = state;
Expand Down
3 changes: 2 additions & 1 deletion lib/features/vpn/server_selection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ class _ServerSelectionState extends ConsumerState<ServerSelection> {
context.showSnackBar(failure.localizedErrorMessage);
}
},
(_) {
(_) async {
await ref.read(serverLocationProvider.notifier).switchToAuto();
appRouter.popUntilRoot();
},
);
Expand Down
Loading