Skip to content

apps_windows: additional Windows app-discovery sources + heuristic noise filter#8710

Merged
atavism merged 108 commits intomainfrom
fisk/apps-windows-additional-discovery
Apr 29, 2026
Merged

apps_windows: additional Windows app-discovery sources + heuristic noise filter#8710
atavism merged 108 commits intomainfrom
fisk/apps-windows-additional-discovery

Conversation

@myleshorton
Copy link
Copy Markdown
Contributor

@myleshorton myleshorton commented Apr 28, 2026

Split out from #8706 — the app-discovery rework half. The base-bug fix that resolves the actual "Failed to fetch installed apps" user reports is at #8709 and merges independently of this PR.

What this is, and why it's separate

The original investigation chased an empty Windows split-tunnel apps list. Two distinct issues turned up:

  1. The actual user-facing bug was GetEnabledApps returning nil (which Dart's as List throws on). One-line fix; lives in lantern-core: fix empty Windows split-tunnel apps list + UI-process logging #8709.
  2. The Windows app-discovery scanner was also genuinely under-covered — it only looked at Start Menu shortcuts and the Uninstall registry, missing browsers / IDEs / Squirrel apps that register elsewhere. This PR is the rework for that.

The two are unrelated in cause and in code. Reviewing them together (originally as #8706) made it hard to evaluate either piece on its own merits.

Changes

  • HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths scanner. Apps that register here so they're runnable via Win+R / shellexecute. Catches browsers, IDEs, Office, and most third-party apps that don't go through Squirrel or Start Menu.
  • HKLM and HKCU \Software\Microsoft\Windows\CurrentVersion\Run. Squirrel-managed apps (Slack, Discord, VS Code Insiders) register for auto-start with command lines pointing at Update.exe --processStart. Same parser as Start Menu .lnk targets.
  • %LOCALAPPDATA%\\\\Update.exe belt-and-suspenders for Squirrel installs that exist on disk under the well-known layout but haven't shown up in the registry yet.
  • isAppPathsNoise filter. App Paths is heavily polluted by Microsoft-bundled tooling (IE relics, Office helpers, vestigial Mail + tablet apps) and UWP package plumbing. Drops entries under known system paths, .NET helper assemblies, anything under \\Microsoft Office\\ except primary product exes, helper-named basenames (substring match), and basenames suffixed with generic helper words.
  • ParentKeyName filter removed from isNonUserFacingUninstallEntry. Introduced in Filter system apps from Windows split tunneling #8641 and over-aggressive — plenty of legitimate end-user apps set it (Squirrel installs, winget packages, MSI bundle children). SystemComponent=1 and NoDisplay=1 are the documented signals; those stay.
  • Start Menu COM-failure logs promoted Debug → Warn (CoInitializeEx, WScript.Shell CreateObject, QueryInterface). These previously failed silently; now an empty result tells us which call broke.
  • Per-filter scan-summary tallies at the end of each scanner: scanned / kept / per-drop-reason counts plus a sample of the first 20 kept apps for triage. Sample paths are filepath.Base'd to avoid PII (full Windows paths typically embed the username).
  • Hot-path constant data hoisted to package scope. isAppPathsNoise's systemPaths / primaryOfficeExes / helperHints / suffix lists were reallocated on every call (runs hundreds-to-thousands of times per scan); now package-level vars.
  • +152 lines of test coverage for the new heuristics.

Risk to be aware of

  • Heuristic filter false positives are the main thing to watch. The generic suffix list (update, service, agent, sync, broker) drops anything ending in those words. A legit user-facing app called "MyApp Service" would be silently hidden from the split-tunnel UI. Tests cover the patterns we thought of; users have unbounded variety. If someone files "my app X is missing from split tunnel" after this lands, that filter is the first place to look.
  • ParentKeyName filter removal changes the uninstall-registry filter behavior slightly. The intent is to recover legitimate apps that were being incorrectly hidden; the risk is now seeing some installer subcomponents we didn't see before. The remaining SystemComponent=1 / NoDisplay=1 filters should still catch most non-user-facing entries.

Test plan

  • gofmt clean.
  • GOOS=windows go vet ./lantern-core/apps/... clean.
  • go test ./lantern-core/apps/... passes locally.
  • CI green.
  • Manual on Windows: confirm previously-missing apps (Slack, Discord, VS Code Insiders, browsers, IDEs) now appear in the split-tunnel "Show Apps" list. Confirm no obvious legit apps are missing that were present before.

🤖 Generated with Claude Code

garmr-ulfr and others added 30 commits March 24, 2026 16:15
Server tags are determined by URL content, not caller-supplied names.
addServerBasedOnURLs now returns the tags of added servers so callers
can connect using the actual tag. Also sends VPN status updates from
connectToServer on Linux so the UI reflects connection state changes.
myleshorton and others added 16 commits April 23, 2026 13:25
Co-authored-by: atavism <atavism@users.noreply.github.com>
* main: don't block first paint on Updater.init()

Moving Updater.init() off the critical path to runApp. Investigating a
one-shot black-screen-on-startup report on a local macOS dev build
(9.0.29 build 487): flutter.log stopped at the last pre-runApp log line
with no Dart exception and no crash, while the Go side kept running
normally. The only awaited call between that last log and runApp is
Updater.init().

Inside init(), the actual update check is already deferred 45 s via
Future.delayed + unawaited. But setFeedURL and setScheduledCheckInterval
are awaited — both bridge into Sparkle via the auto_updater Flutter
plugin, and both can stall on first launch: feed URL resolution,
keychain access, or a previous launch's background worker still holding
a lock. Any of those becomes a main-isolate hang that prevents runApp,
which exactly matches the observed symptom.

Fix: drop the await so Updater.init() runs concurrently with the rest
of startup. All errors are already handled inside init() itself, so
unawaited is safe.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* review: guard sl<Updater>() lookup against failed service injection

Copilot flagged that if injectServices() throws above (caught at
main.dart:45), Updater is never registered (it's registered at
injection_container.dart:40, after storage init), and sl<Updater>()
throws synchronously. unawaited() doesn't help — the throw happens
before the Future is constructed, so it propagates out of main and
prevents runApp.

Wrap the call in try/catch + sl.isRegistered<Updater>() so any failure
to look up or start Updater.init logs and continues to runApp.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the FFI path to radiance's ipc.Client.TailLogs and merges in-app
flutter.log records so the diagnostic logs view shows both sources.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up:
- refactor(vpn): own VPN status on the client so restarts span tunnels
- vpn: instrument tunnel.start phases + VPNClient.Restart (#443)

The VPN-status-ownership refactor moves setStatus calls out of
tunnel and onto VPNClient so a restart transitions Restarting →
Disconnecting → Disconnected → Connecting → Connected cleanly.

The instrumentation PR adds child spans around libbox.Setup,
libbox.NewServiceWithContext, libbox.BoxService.Start, and
newMutableGroupManager so SigNoz can attribute the 10s+ tail
on /service/start observed in Freshdesk #173696.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nnel (#8702)

* lantern-core: dispatch ConnectVPN to SelectServer on live tunnel

When the Flutter UI triggers an auto-select on a live tunnel — most
visibly Jigar's rewrite of onSmartLocation (server_selection.dart), which
routes "switch back to Smart" through startVPN(force: true) → Dart
lantern.startVPN() → ffi.go:startVPN → c.ConnectVPN("") — radiance's
/vpn/connect endpoint rejects the request with ErrTunnelAlreadyConnected
(radiance/vpn/vpn.go:126 in VPNClient.Connect). The error is returned to
the Dart UI as a snackbar, the tunnel stays pinned to the previously
selected manual server, and lantern.log is silent because neither
LocalBackend.ConnectVPN nor VPNClient.Connect slog the ErrTunnelAlready
Connected path.

Observed on 9.0.30 beta (internal tester, Freshdesk #173763, build from
commit 4054689 which includes Jigar's 2895072). After manually
picking Bogotá, clicking "Smart" at the top of the server-selection
screen surfaces the snackbar and the tunnel keeps routing traffic
through the Bogotá samizdat outbound.

Fix: when Status() == Connected, LanternCore.ConnectVPN dispatches the
request to /server/selected (the live-tunnel outbound swap) instead of
/vpn/connect. Empty tag normalizes to vpn.AutoSelectTag — Dart sends ""
for Smart, radiance recognizes only the literal "auto" and otherwise
falls into the manual-outbound branch of SelectServer, stranding Clash
in manual mode with an empty selector. The mapping is centralized in a
small normalizeAutoTag helper used by both ConnectVPN and SelectServer.

This puts the same dispatch logic that lives in ffi.go:connectToServer
onto every caller of LanternCore.ConnectVPN — including ffi.go:startVPN
(which Jigar's rewrite now funnels through) and any future FFI/mobile
entry point.

getlantern/engineering#3291 issue 3. Supersedes earlier work on
fisk/connect-dispatch-select-when-connected (485bf5a), which was
scoped to this same dispatch but predated the current refactor branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* vpn_tunnel: dispatch StartVPN to SelectServer on live tunnel (mobile path)

Mobile.StartVPN (the gomobile entry point for Android MainActivity and
iOS VPNManager) routes through vpn_tunnel.StartVPN(client), which calls
client.ConnectVPN(ctx, vpn.AutoSelectTag) directly — bypassing
lanterncore.Core. Jigar's onSmartLocation rewrite dispatches "switch
back to Smart" through startVPN(force: true), which on Android/iOS
lands here. Same ErrTunnelAlreadyConnected bug as the FFI path fixed in
the previous commit.

Mirror the VPNStatus dispatch pattern garmr already added to
vpn_tunnel.ConnectToServer in 4054689: when Status() == Connected,
swap outbound via /server/selected; otherwise fall through to the
existing /vpn/connect start.

Together with the LanternCore.ConnectVPN dispatch, this closes the
Smart-from-connected bug on every platform (Windows FFI, Android/iOS
gomobile).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ffi: drop now-redundant VPNStatus dispatch in connectToServer

LanternCore.ConnectVPN already routes to /server/selected when the
tunnel is live (added earlier in this PR), so ffi.go:connectToServer's
own VPNStatus check is duplicate work. Collapse to a single c.ConnectVPN
call — both the live-tunnel-swap and fresh-connect paths flow through
the dispatch one layer down.

Behavior unchanged. The "start service failed" error wrapper is kept
for Dart-side snackbar stability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* lantern-core: collapse dispatch to a single implementation in vpn_tunnel

Three functions had independent VPNStatus → SelectServer-vs-ConnectVPN
dispatches after the earlier commits: LanternCore.ConnectVPN,
vpn_tunnel.StartVPN (both added in this PR), and vpn_tunnel.ConnectToServer
(pre-existing from 4054689). Consolidate so vpn_tunnel.ConnectToServer
is the authoritative dispatch and the other two delegate.

- LanternCore.ConnectVPN → vpn_tunnel.ConnectToServer(lc.client, tag)
- vpn_tunnel.StartVPN → ConnectToServer(client, vpn.AutoSelectTag)

LanternCore.SelectServer keeps its own empty-tag normalization since its
scope is the one-shot SelectServer IPC, not the dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…089) (#8703)

Patrick's radiance fac9089 ("fix(vpn): treat the empty string as
AutoSelect in SelectServer") is now pinned on this branch via
72a6c62. Radiance normalizes tag == "" → AutoSelectTag on both
ConnectVPN and SelectServer, so the client-side normalizations we
added earlier (normalizeAutoTag helper in core.go, `if tag == ""` in
vpn_tunnel.ConnectToServer) are redundant — radiance handles the Dart
"" convention uniformly.

Remove:
- LanternCore.normalizeAutoTag helper + its use in SelectServer
- `if tag == "" { tag = vpn.AutoSelectTag }` branch in
  vpn_tunnel.ConnectToServer
- lantern-core/core_test.go (only tested the removed helper)

Behavior unchanged end-to-end: empty tag still means auto-select on
every path (FFI, gomobile, connectToServer, startVPN).

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Server empty-tag fix (#8705)

radiance@d5a1872 completes fac9089's empty-string → AutoSelectTag
normalization by extending it to LocalBackend.SelectServer, which
previously only matched the literal "auto" and fell through to the
srvManager lookup for tag == "" — producing "no server found with tag"
(HTTP 500, snackbar) on Smart-from-connected flows after the client-
side normalization was removed in this branch's 6de3c9a.

Reported on Lantern 9.0.30 beta via Freshdesk #173773.

go.mod + go.sum bump only; no lantern code changes. Pinned commit:
getlantern/radiance@d5a18726afbc (#444).

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(logs): stream diagnostic logs via ipc TailLogs on mobile

Adds a mobile gomobile binding for ipc.Client.TailLogs (TailLogs +
LogSubscription) and switches Android and iOS to consume it, replacing
the per-platform log-file tailers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(logs): stream diagnostic logs via ipc TailLogs on macos

Switches the macOS log stream to MobileTailLogs, matching iOS. Removes
the file-watching LogTailer (no remaining callers).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(logs): harden TailLogs against nil, panics, and listener leaks

- Reject nil listener in mobile.TailLogs; recover from panics crossing
  the gomobile bridge so the stream survives unexpected bridge errors.
- Retain the Kotlin LogListener in a field so the Go side's reference
  stays strongly rooted on the JVM.
- On iOS/macOS, cancel any pre-existing subscription before starting a
  new one and clear the stored listener when MobileTailLogs errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(logs): share TailLogs plumbing across mobile and ffi

Adds lantern-core/logs.Subscribe wrapping ipc.Client.TailLogs so the
mobile and desktop integrations go through one helper. Drops the iOS
LogTailer dead code and the unused lantern-core/logging package.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update log formatting

* Fix issue with ios

* Fix macos logs issue

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Jigar-f <jigar@getlantern.org>
…r-operation timeouts (#8707)

* ffi: skip the daemon-reachability preflight on Windows / macOS / mobile

The 300 ms preflight in lantern-core/core.go's CheckDaemonReachable
was originally tuned for the Linux flow (PR #8494 by atavism, commit
bf054f4), where the failure path falls back to `systemctl is-active
lanternd.service` for a rich diagnostic error. The 300 ms cap made
sense as "fast probe → systemd-rich-error", with the systemd query
adding the actual user-facing context.

Subsequent refactors (commit bd89bea Apr 7, then PR #8578 commit
4d4e06d Apr 16) generalized that preflight to all platforms but
the systemd fallback only survived in ffi_linux.go. On Windows /
macOS / mobile, ffi_nonlinux.go ended up running the same 300 ms
probe with no fallback — just an artificial guillotine in front of
ConnectVPN, which has its own "lanternd not reachable" error path
with equivalent precision.

Cold-start IPC on Windows regularly exceeds 300 ms (named-pipe dial
+ winio impersonation token dance + H2c connection preface +
goroutine scheduling on a 96-second-idle daemon), so the first VPN
toggle after launch reliably trips the timeout and shows the user a
"lanternd not reachable" error. Clicking again 10 seconds later
silently succeeds. Reproduced on the same Windows machine across
9.0.29 (Freshdesk #173696) and 9.0.30 (#173932).

Make the preflight a no-op on non-Linux. Linux keeps the original
fast-probe-then-systemdDiag flow unchanged. If we add Windows
(`sc query LanternSvc`) or macOS (`launchctl list`) diagnostics
later, restore the preflight and call them from here.

See getlantern/engineering#3382 for the full archaeology + design
discussion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ffi + lantern-core: bound IPC calls with per-operation timeouts

Companion to dropping the non-Linux daemon-reachability preflight in this
same PR. The preflight (ffi_nonlinux.go's `checkDaemonReachable`) was
introduced in commit bd89bea along with the *removal* of per-call
timeouts that used to live on the FFI layer:

    -    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    -    if err := c.Client().DisconnectVPN(ctx); err != nil { ... }
    +    if err := c.DisconnectVPN(); err != nil { ... }

After that change, the only IPC call with any deadline at all was the
300 ms preflight. Every other operation flowed lc.ctx (
context.WithCancel(context.Background())) straight through, meaning a
hung lanternd would freeze the UI indefinitely. Dropping the preflight
without restoring per-call timeouts removes the only line of defense.

Restore them at the LanternCore layer where they belong, with values
sized for the inherent work each operation does (state changes can run
into multi-second territory; status queries should be near-instant):

    ipcConnectTimeout     = 60 * time.Second   // ConnectVPN
    ipcStateChangeTimeout = 30 * time.Second   // SelectServer, DisconnectVPN
    ipcStatusTimeout      = 10 * time.Second   // VPNStatus, IsVPNRunning

These bound the worst case (hung daemon → user sees a clear error within
a minute, no indefinite spinner) without firing during normal slow paths.
The dialer's 10 s connect timeout (radiance/ipc/conn_windows.go) already
covers the lanternd-crashed case; these guard the lanternd-hung case.

vpn_tunnel.{StartVPN, StopVPN, ConnectToServer} take the ctx through
their signatures instead of building their own context.Background()
internally, so callers stay in charge of their own deadlines. mobile/
mobile.go updated to set 60 s / 30 s / 60 s contexts on its three
gomobile entry points.

CheckDaemonReachable's 300 ms timeout is kept untouched — Linux still
calls it from ffi_linux.go for the systemctl is-active fallback that's
the whole point of the fast probe.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ise filter

Split out from #8706 — held back from the base-bug fix branch
(fisk/fix-empty-apps-base-bug) because this is the heuristic part of
that PR, not the actual cause of Freshdesk #173774 / #173778 / #173826.
The base bug was GetEnabledApps returning nil-as-"null" instead of
empty-as-"[]" (fixed in the other branch); this PR is the broader
investigation that grew alongside the diagnosis but stands on its own
merits.

What's in here:

- **HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths scanner.**
  Apps that register here so they're runnable via Win+R / shellexecute.
  Catches browsers, IDEs, Office, and most third-party apps that don't
  go through Squirrel / Start Menu.

- **HKLM and HKCU \Software\Microsoft\Windows\CurrentVersion\Run.**
  Squirrel-managed apps (Slack, Discord, VS Code Insiders) register for
  auto-start with command lines pointing at Update.exe --processStart.
  Same parser as Start Menu .lnk targets, including --processStart.

- **%LOCALAPPDATA%\<App>\Update.exe pattern.** Belt-and-suspenders for
  Squirrel installs that don't show in the registry yet but exist on
  disk under the well-known layout.

- **isAppPathsNoise filter.** App Paths is heavily polluted by
  Microsoft-bundled tooling (IE relics, Office helpers, vestigial Mail
  + tablet apps) and UWP package plumbing (winget,
  WindowsPackageManagerServer). Drops entries under known system
  paths, .NET helper assemblies, anything under \Microsoft Office\
  except primary product exes, helper-named basenames (substring
  match: "browsersupport", "lastpassexporter", "updater", "helper",
  "diagnostic", "diagcmd"), and basenames suffixed with the generic
  helper words (update, service, agent, sync, broker).

- **ParentKeyName filter removal in isNonUserFacingUninstallEntry.**
  ParentKeyName != "" was introduced in #8641 and was over-aggressive
  — plenty of legitimate end-user apps set it (Squirrel installs,
  winget packages, MSI bundle children). SystemComponent=1 and
  NoDisplay=1 are the documented signals; those stay. Drops the
  now-unused parentKeyName field on uninstallEntryMetadata too.

- **COM-failure logs in Start Menu scanner promoted Debug → Warn**
  (CoInitializeEx, WScript.Shell CreateObject, QueryInterface). These
  paths previously failed silently; now an empty result tells us
  which call broke.

- **Per-filter scan-summary tallies** at the end of each scanner with
  scanned/kept/per-drop-reason counts and a sample of kept apps for
  triage. Sample paths are redacted to filepath.Base to avoid
  including user PII (full Windows paths typically embed the
  username) in scan logs that get bundled into Report Issue tickets.

- **Hot-path constant data hoisted to package scope.**
  isAppPathsNoise's systemPaths / primaryOfficeExes / helperHints /
  suffix lists used to be reallocated on every call (runs hundreds-
  to-thousands of times per scan). Now package-level vars.

- **+152 lines of test coverage** for the new heuristics in
  apps_windows_test.go.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
myleshorton and others added 6 commits April 28, 2026 06:54
…ogging (#8709)

Two narrow fixes that together resolve Freshdesk #173774 / #173778 /
#173826 (Derek's "Failed to fetch installed apps" empty list on Windows
split tunneling). Split out from #8706 so they can land independently
of the broader app-discovery rework that PR also contained.

1. **GetEnabledApps returns []string{} instead of nil.**
   When no apps are split-tunneled, the previous code returned nil,
   which json.Marshal serialized as "null". Dart's jsonDecode("null")
   returns null; the receiving code does `as List`, which throws and
   the UI shows "Failed to fetch installed apps". Initializing as an
   empty slice serializes to "[]" — Dart parses that as an empty list,
   no exception, no error UI. THIS is the actual root cause of the
   empty-list reports we've been chasing; the apps-discovery scanner
   work was investigating a different (also-real but secondary) issue.

2. **UI-process slog wired up via common.Init.**
   On the refactor branch, the UI process never called common.Init.
   slog wrote to stderr (= nowhere on a GUI host), settings were
   uninitialized, no lantern.log was produced outside the daemon.
   Patrick caught this — it was a one-line miss in the refactor.

   Platform-aware so we don't double-init on platforms where the
   backend embeds in-process:
     - windows/linux: full common.Init (separate UI + daemon procs)
     - darwin/ios:    setupAppLogging into a distinct lantern-app.log
                      so the main-app slog doesn't race the tunnel
                      extension's lantern.log on lumberjack rotation
     - android:       Mobile.SetupRadiance already ran common.Init
                      upstream — fall through

3. **Auto-attach UI-process *.log to ReportIssue (windows/linux only).**
   Without it the daemon's archive glob only sees the daemon's logDir;
   UI-side lantern.log + flutter.log never reach the issue bundle. The
   daemon runs as SYSTEM on Windows; we keep UI logDir at
   %PUBLIC%\Lantern\logs so SYSTEM can read it.

The broader Windows app-discovery work from #8706 (App Paths scan, Run
keys, Squirrel pattern, isAppPathsNoise heuristic filters) is being
held in a separate PR for independent review.

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…en't lost (#8711)

On Android the entire app runs in a single process, so once common.Init
runs slog.SetDefault covers everything. But common.Init only runs deep
inside SetupRadiance / StartIPCServer, which LanternVpnService launches
asynchronously from an intent fired by MainActivity.startLanternService.
Any slog call emitted in the gap — including any of the wide MethodHandler
surface that Flutter can reach before the VPN service is up — falls
through to the stdlib default (text → stderr → logcat at INFO), so DEBUG
logs vanish and the format diverges from what we use everywhere else.

Add Mobile.InitLogging as a thin gomobile-exposed wrapper around
common.Init, and call it from MainActivity.configureFlutterEngine before
startLanternService. common.Init is guarded by an atomic.Bool, so the
later call from backend.NewLocalBackend is a no-op.

Mirrors PR #8709 (Windows). Reported on Slack by Jigar.

Co-authored-by: Adam Fisk <afisk@mini.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-refactor

# Conflicts:
#	lib/core/windows/pipe_client.dart
#	lib/lantern/lantern_ffi_service.dart
#	lib/lantern/lantern_windows_service.dart
Base automatically changed from garmr/radiance-daemon-refactor to main April 28, 2026 18:20
…itional-discovery

# Conflicts:
#	go.mod
#	go.sum
#	lib/features/auth/confirm_email.dart
#	lib/features/home/provider/app_event_notifier.g.dart
#	lib/features/home/provider/home_notifier.g.dart
#	lib/features/logs/provider/diagnostic_log_notifier.g.dart
#	lib/features/vpn/provider/available_servers_notifier.g.dart
#	lib/features/vpn/provider/server_location_notifier.g.dart
@atavism
Copy link
Copy Markdown
Contributor

atavism commented Apr 29, 2026

LGTM

@atavism atavism merged commit fcf6d1a into main Apr 29, 2026
9 checks passed
@atavism atavism deleted the fisk/apps-windows-additional-discovery branch April 29, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants