getlantern / getlantern/lantern

[Bug]: macOS split tunneling does not support iOS apps (Mac App Store "iPhone & iPad Apps")

Open
#9,064 1 comment 0 reactions 1 assignee View on GitHub

@jigar-f is already working on this.

Since Sep 16, 2026.

Dominant language
Dart
Stars
16k
Forks
11k
Avg merge
2d 4h
Merged PRs (30d)
60

Description

What happened?

On macOS, the split tunneling app list only contains native macOS apps. Apps installed from the Mac App Store's "iPhone & iPad Apps" section never appear in the list, so they cannot be added to split tunneling.

Beyond the missing entry, the generated rule could never match even if such an app were listed, because the macOS matching rule hardcodes a Contents/ path segment that iOS app bundles do not have.

Version

Observed on Lantern 9.1.20 on macOS 26.6.2. The same code path is present on main.

Which platform(s) does this affect?

macOS

Steps to reproduce
  1. Install any app from the Mac App Store under the "iPhone & iPad Apps" section.

  2. Open Lantern → Settings → Split Tunneling, and turn it on.

  3. Open the Apps list.

  4. The iOS app is not listed. There is no way to add it.

You can confirm the bundle layout without Lantern:

ls "/Applications/<Any iOS app>.app/"

It prints WrappedBundle and Wrapper, and no Contents directory.

Why this happens

1. iOS apps on macOS use a different bundle layout.

App type Layout
Native macOS app Foo.app/Contents/{Info.plist, MacOS/}
iOS app on macOS Foo.app/Wrapper/Bar.app/{Info.plist, }

An iOS app bundle has no Contents/ directory and no top-level Info.plist. The real bundle, its Info.plist and its executable live one level deeper, inside Wrapper/. Note that the top-level directory name is the localized display name and does not necessarily match the inner bundle name (Foo.app wraps Bar.app), so the correct path cannot be inferred from the directory name alone.

A concrete layout looks like this:

/Applications/Foo.app/
├── WrappedBundle/
└── Wrapper/
  ├── BundleMetadata.plist
  ├── iTunesMetadata.plist
  └── Bar.app/
      ├── Bar           <- executable, directly in the bundle root
      ├── Info.plist
      ├── Frameworks/
      └── PlugIns/

2. The macOS split tunneling rule hardcodes Contents/.

In lib/features/split_tunneling/provider/apps_notifier.dart:

/// For macOS, we need to use regex to match the app path
String appPath(AppData appData) {
 if (PlatformUtils.isMacOS) {
   return '${appData.appPath}/Contents/.*';
}
...
}

and:

SplitTunnelFilterType getFilterType() {
 if (PlatformUtils.isMacOS) {
   return SplitTunnelFilterType.processPathRegex;
} else if (PlatformUtils.isWindows) {
   return SplitTunnelFilterType.processPath;
}
 return SplitTunnelFilterType.packageName;
}

For an iOS app installed at /Applications/Foo.app, the generated rule is:

/Applications/Foo.app/Contents/.*

but the actual executable path is:

/Applications/Foo.app/Wrapper/Bar.app/Bar

The regex cannot match. The real path has an extra Wrapper/Bar.app/ segment and contains no Contents/ at all. This is not a matter of imprecise matching — the pattern can never hit.

3. The app list likely drops them during enumeration.

The installed-app list is streamed from the core over FFI (LanternService.appsDataStream()lanternService.appsDataStream()). If enumeration reads <bundle>/Contents/Info.plist to obtain the display name and bundle id, iOS apps would be skipped because that file does not exist for them. I could not confirm this part directly, as the enumeration code does not live in this repository — please correct me if it works differently.

Impact
  • Under Smart Routing (the default), impact is limited, because routing decisions are largely domain-based and most local domains are already covered by the bypass lists.

  • Under full tunnel / proxy all, there is no way to exclude a Mac-hosted iOS app from the tunnel. The user cannot make it connect directly.

Suggested fix direction

The macOS rule should stop assuming a Contents/ segment. Possible approaches:

  • Match both layouts, e.g. <path>/Contents/.* and <path>/Wrapper/.*.

  • Or match against the resolved executable path instead of a fixed subdirectory.

  • Ensure app enumeration also handles bundles whose Info.plist is not under Contents/, and reads it from Wrapper/<Inner>.app/Info.plist when present.

Environment
  • macOS: 26.6.2

  • Lantern: 9.1.20

  • Install source: official .dmg from the releases page

Version

No response

Which platform(s) does this affect?
  • Android
  • iOS
  • Desktop (Windows)
  • Desktop (MacOS)
  • Desktop (Linux)
Related Support Tickets

No response

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.