Repository navigation
fix(macos27): embed Bluetooth usage Info.plist in daemon/CLI, re-sign installed daemon - #41
Merged
HMAKT99 merged 1 commit intoOct 30, 2026
Conversation
… after install Plain SwiftPM executables have no Info.plist, so CoreBluetooth cannot read NSBluetoothAlwaysUsageDescription. On macOS 27 this leaves TCC authorization incomplete: touchbridged logs 'Bluetooth powered off' and touchbridge-test never receives a poweredOn callback, silently stalling pairing. Embed a minimal Info.plist into the touchbridged and touchbridge-test executables via -sectcreate __TEXT __info_plist. Because the linker-signed signature of such a binary can be rejected after being copied, install.sh now runs an ad-hoc codesign pass on the installed daemon. Mac side of PR HMAKT99#37 by @yachoukh; adapted to v2.0.0 (the companion-side resetConnectionAndScan change is already covered by beginPairing -> unpair, which stops scanning before a new service UUID is used).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(macos27): embed Bluetooth usage Info.plist in daemon/CLI, re-sign installed daemon
Summary
Supersedes #37 by @yachoukh, rebased onto
v2.0.0(c4c746f). On macOS 27("Golden Gate") the shipped
v2.0.0build cannot use Bluetooth from eitherSwiftPM executable, which blocks pairing and therefore every privileged
authentication path. Both failures were reproduced on real hardware and fixed
here:
Info.plist. Plain SwiftPM executables carry no__info_plistsection, so CoreBluetooth has nowhere to readNSBluetoothAlwaysUsageDescriptionfrom. On macOS 27 this leaves the TCCauthorization request incomplete (
TCC complete 0, no manager statecallback):
touchbridgedlogsBluetooth powered off, andtouchbridge-test pairnever receives anyCBManagerStateat all, so theCLI silently sits on "Waiting for companion device to connect" with the
phone stuck on "Scanning for Mac…".
linked with
-sectcreate __TEXT __info_plist, the binary's ownlinker-signed ad-hoc signature is rejected (
Code Signature Invalid, hardSIGKILL) after being copied to/usr/local/binon some macOS/Xcodeversions.
Changes
daemon/Package.swift: embed a minimalInfo.plist(bundle identifier,name,
NSBluetoothAlwaysUsageDescription,NSBluetoothPeripheralUsageDescription) into thetouchbridgedandtouchbridge-testexecutables via-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist. The plist pathis resolved from
#filePath, so builds work from any working directory.daemon/Sources/touchbridged/Supporting/Info.plist(new)daemon/Sources/touchbridge-test/Supporting/Info.plist(new)scripts/install.sh: after copying the daemon to/usr/local/bin, re-signit with a plain ad-hoc
codesign --force -s -pass (best effort).Not carried over from #37
The companion-side
CompanionCoordinator.beginPairing()change(
startScanning()→resetConnectionAndScan()) is not included, becausev2.0.0changed the pairing lifecycle to fix the same failure differently:beginPairingnow callsunpair()first, andunpair()callsbleClient.stopScanning()+disconnect()before the new service UUID isscanned. A stale mid-scan state for an old UUID is therefore already cleared
before the next scan. If the maintainers prefer the explicit
resetConnectionAndScan()call as defence in depth, it is a one-line follow-up.Testing
Environment: macOS 27.0.1, Xcode 27.1, Swift 6.4 (Apple Swift 6.4), real
iPhone 18 Pro (iOS 27.2) with a Personal Team signature.
protocol: 25 tests pass (9 XCTest + 16 Swift Testing).daemon: 115 tests pass (18 XCTest + 97 Swift Testing).make -C pam test: native evidence tests + PAM script migration /idempotence / symlink fixtures pass.
tests/e2e-validation.sh: isolated suite passes; menubar protected-pathtest and disabled Authorization Plugin entry check pass.
TCC available 1, req 1 complete 0, nopoweredOncallback;Bluetooth powered on→TouchBridge GATT service registered→Advertising started(daemon), and the CLI advertises the pairingservice instead of hanging silently.
codesign --verify --strictpasses and the binary runsfrom
/usr/local/bin.fingerprint enrollment (
touchbridge-trust enroll) → PAM activation via/etc/pam.d/sudo_local→sudoapproved by iPhone Face ID; deny/timeoutfalls back to the password prompt and never grants.
c4c746fviagit am(verified on a fresh clone).Note for automated installers
On macOS 27, a root shell obtained through Authorization Services
(
osascript … with administrator privileges) was observed to fail creatingfiles under
/etc/pam.dwithEPERM, while an interactive Terminalsudosucceeds. Installers that write PAM hooks through GUI authorization may need
to run that step in a Terminal (or document it). This is an environment
behaviour, not a code change in this PR.