Skip to content

fix(xpc): make MenuBarItemService work for ad-hoc-signed builds on macOS 26#950

Open
pdurlej wants to merge 2 commits into
jordanbaird:macos-26from
pdurlej:pr/xpc-isfromsameteam-guard
Open

fix(xpc): make MenuBarItemService work for ad-hoc-signed builds on macOS 26#950
pdurlej wants to merge 2 commits into
jordanbaird:macos-26from
pdurlej:pr/xpc-isfromsameteam-guard

Conversation

@pdurlej

@pdurlej pdurlej commented May 25, 2026

Copy link
Copy Markdown

What

Restores MenuBarItemService XPC connectivity for community ad-hoc-signed builds of Ice on macOS 26 Tahoe.

Why

On macOS 26, the new XPCSession.setPeerRequirement(.isFromSameTeam()) API silently rejects every peer when the calling process has no Team Identifier — which is the case for any ad-hoc-signed build (every community fork, every xcodebuild from source). The current Listener.swift and MenuBarItemServiceConnection.swift unconditionally apply this requirement, so:

  • The XPC listener is created but rejects the parent app's connection
  • The client Connection.sourcePID(...) always returns nil
  • MenuBarItemManager.uncheckedCacheItems logs Missing sourcePID for ... every few seconds
  • The Menu Bar Layout settings pane has no PIDs to render — shows empty rows / blank list

This is the upstream class causing:

Combined: ~130 reactions across the bug class.

How

Introduces a shared MenuBarItemService.ownTeamIdentifier() helper in Shared/Services/MenuBarItemService.swift that returns the running process's Team Identifier via SecStaticCode, or nil if the binary is ad-hoc-signed.

Both sides of the XPC handshake gate their .isFromSameTeam() enforcement on ownTeamIdentifier() != nil:

  • MenuBarItemService/Listener.swift — listener falls back to unrestricted activation when no Team ID is available
  • Ice/MenuBar/MenuBarItems/MenuBarItemServiceConnection.swift — client skips setPeerRequirement(.isFromSameTeam()) when no Team ID is available

Developer-ID-signed builds (App Store / future TestFlight / signed releases) keep strict same-team enforcement automatically — the helper returns the real Team ID and the requirement applies on both sides. Only community ad-hoc builds skip the requirement, which is the only configuration that can't satisfy it anyway.

Testing

Verified on:

  • macOS 26.5 Tahoe, MacBook Pro Apple Silicon
  • Ad-hoc-signed build (xcodebuild from source): Menu Bar Layout pane populates correctly, all three sections (Visible / Hidden / Always-Hidden) render menu bar items
  • Developer-ID-signed + Apple-notarized build: same behavior, strict same-team requirement still enforced (verified via codesign -dv showing both peers signed by same Team ID)

A working signed + notarized DMG that ships these commits is available for reviewers who want to test without rebuilding: https://github.com/pdurlej/Ice/releases/latest (same com.jordanbaird.Ice bundle ID, drop-in replacement).

Notes

This PR is offered in good faith to the upstream repo — the same fix has been deployed in a community fork (pdurlej/Ice, "Fire from Ice") since 2026-05-24 and confirmed working by multiple users on macOS 26.4–26.5. Happy to iterate on the patch shape if a different approach is preferred.

Closes (or contributes toward closing): #744, #891, #913, #846, #818, #832, #872.

pdurlej added 2 commits May 26, 2026 00:48
Upstream PR jordanbaird#903 introduced MenuBarItemService.xpc with this line:

    listener = try XPCListener(service: name,
                               requirement: .isFromSameTeam()) { ... }

`.isFromSameTeam()` requires the listener and every peer to be signed
with the same Apple Team Identifier. That works for builds Jordan
produces from his Developer ID Application certificate. It does NOT
work for any ad-hoc-signed build (TeamIdentifier = empty), because
empty-vs-empty is never treated as a match — the listener rejects
every check-in attempt with "Bogus check-in attempt. Ignoring." and
"Dropping check-in message due to code signing requirement".

The visible symptom is the Menu Bar Layout settings pane spinning
forever on "Loading menu bar items…" — XPC never returns, so Ice
never gets the cached item snapshots. This is the same class of bug
reported in upstream issues jordanbaird#744 (46 reactions) and jordanbaird#891 (30 react-
ions), and it bites every community fork that ships without an Apple
Developer Program account — which is roughly every community fork.

This commit reads the running process's actual Team Identifier via
SecCodeCopySigningInformation; if it's nil (ad-hoc / unsigned), we
fall through to the no-requirement activation path. If it's set
(properly Developer-ID-signed build), behaviour is unchanged — the
strict same-team requirement still applies.

For our fire fork:
  - 0.11.13-fire.0..fire.1 (ad-hoc, this branch's defaults) → fixed.
  - 0.11.13-fire.2+ once signed with our Developer ID → unchanged,
    still uses .isFromSameTeam() because we'll have a team ID.

Also bumps MARKETING_VERSION 0.11.13-fire.1 → 0.11.13-fire.2 and
CURRENT_PROJECT_VERSION 1123 → 1124 so Sparkle in installed fire.1
recognizes this as a newer build and offers the update.
Companion to the listener-side fix in the previous commit. The
XPCSession client (Ice/MenuBar/MenuBarItems/MenuBarItemServiceConnection.swift)
also defaults to .isFromSameTeam() peer requirement, which silently
rejects ad-hoc-signed builds the same way the listener did.

The refactor introduces MenuBarItemService.ownTeamIdentifier() in the
shared layer so both sides query exactly the same predicate. Developer-
ID-signed builds keep strict same-team enforcement; ad-hoc community
builds skip both requirements (Listener accepts, Client sends).
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.

1 participant