Skip to content

fix: always check the peer (HOLDS_ONE) before reclaiming, even when bonded#41

Merged
MegaManSec merged 1 commit into
mainfrom
fix/always-check-peer-before-reclaim
Jun 3, 2026
Merged

fix: always check the peer (HOLDS_ONE) before reclaiming, even when bonded#41
MegaManSec merged 1 commit into
mainfrom
fix/always-check-peer-before-reclaim

Conversation

@MegaManSec
Copy link
Copy Markdown
Owner

Problem (regression from #40)

#40 let the auto-reconnect watcher skip the HOLDS_ONE peer check for a peripheral still bonded to this Mac, on the premise that "the peer can'''t hold a device whose link key lives here."

That premise is wrong. Apple'''s Magic devices remember multiple hosts and stay bonded to both Macs at once (the README'''s setup step pairs each peripheral to both, and they only connect to one at a time). So isPaired() being true here says nothing about which Mac is currently connected.

The hole this opened

  1. This Mac holds the keyboard, then sleeps (lid close) without releasing it (releaseOnSleep off, or no active peer at sleep time) — so it stays bonded.
  2. During the downtime the user moves the keyboard to the other Mac via its Magic Switch. The other Mac can'''t reach a sleeping Mac to make it -remove, so it pairs locally — and this Mac'''s bond persists.
  3. This Mac wakes, the watcher reclaims everything it was holding. With the skip, it reconnects the keyboard without asking → it yanks the keyboard back from the Mac that legitimately holds it.

Answering the actual question that surfaced this: before reclaiming, does it check whether the peer claimed the peripheral? — after #40 the answer was "no, not when bonded." It should always be "yes."

Fix

Revert the bonded-device skip so every reclaim goes through reclaimIfPeerIsFreeHOLDS_ONE first. The gentle reconnect from #40 stays: connectPeripheral still opens the connection instead of re-pairing a bonded device (that is what fixed the original re-pair/disconnect loop), but it now only runs after the peer confirms it isn'''t holding the peripheral.

Testing

  • xcodebuild ... CODE_SIGNING_ALLOWED=NO build succeeds; swift format lint clean.
  • Power-cycle reclaim still works (peer says not-holding → gentle reconnect, no re-pair loop).
  • Contested case: peer holding the peripheral → HOLDS_ONE success → this Mac stands down instead of stealing it.

…onded

#40 let the auto-reconnect watcher skip the HOLDS_ONE peer check for a
peripheral still bonded to this Mac, on the premise that "the peer can't
hold a device whose link key lives here." That premise is wrong: Apple's
Magic devices remember multiple hosts and stay bonded to *both* Macs (the
README's setup step pairs each peripheral to both), so being paired here
says nothing about which Mac is currently connected.

The regression: if this Mac drops/sleeps while holding a peripheral and
the peer takes it during the downtime (the peer can't reach a sleeping
Mac to make it `-remove`, so the bond persists here), this Mac would wake
and reconnect-reclaim it without asking — yanking it back from the peer
that legitimately holds it.

Revert the skip so every reclaim goes through `reclaimIfPeerIsFree`
first. The gentle reconnect from #40 stays (connectPeripheral opens the
connection instead of re-pairing a bonded device), but it now only runs
after HOLDS_ONE confirms the peer is free — which is what actually fixed
the reported re-pair/disconnect loop.
@MegaManSec MegaManSec merged commit cdd00a8 into main Jun 3, 2026
2 checks passed
@MegaManSec MegaManSec deleted the fix/always-check-peer-before-reclaim branch June 3, 2026 15:27
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

🎉 This PR is included in version 2.11.4 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant