cmux-next phone access: one owner for the phone list's user; the Allow question shows the key

TE
16d ago
+340-35

Stacked on #21898 (held for Leo); both changes only exist there. Base is feat/next-phone-allowlist so this diff is only the follow-ups; after #21898 lands, retarget to feat-cmux-next and merge base once.

Summary

Two follow-ups from the #21898 re-review.

One owner for the phone list's user.

  • Before this change, two places set whose list it was:
    • the iPhone host's account, via IPhoneHostService+Pairing.swift:32 pairedStore.load;
    • the Iroh listener, via PhoneAdmission.setUser, which MobileHostService.start and stop called.
  • When a team went away while you were signed in, the Iroh side signed the list out while the iPhone host kept it.
  • Now only IPhoneHostService.accountChanged → loadPaired → MobileHostPairedStore.load decides the user. PhoneAdmission only reads it.
  • These behave as before:
    • signed out: an empty list, and nobody is admitted or asked;
    • switching user: reloads the list and drops held denials;
    • signing out: forgets that user's phones.
  • An Allow answered after a user switch is now refused and not saved, by three checks:
    • each open question is tied to the user it was asked for, so a phone that reconnects as another user while it is open doesn't join it; that connection is refused, and the phone is asked again once the question closes;
    • PhoneAdmission.decide checks the list's user again once the answer arrives;
    • MobileHostPairedStore.change(for:) refuses an edit whose user no longer matches.
  • PhoneAdmission starts from the list's current user. It may be created after the account has already loaded the list, and before this fix the first same-user change then looked like a user switch and cleared every Deny hold.

The Allow question shows the key. The Iroh Allow dialog now shows "Device key " above the message, so you can tell phones with the same name apart.

  • The fingerprint is MobileHostPairedDevices.fingerprint of the phone's endpoint key: the same 16 hex characters as that phone's Iroh row in Settings > Mobile (IPhoneHostService+Page.swift:85). The hostd pair prompt fingerprints the approval key instead.
  • A key that isn't hex gets no key line, rather than the fingerprint of empty data.
  • The new string phoneAccess.allow.key has all 21 locales, copied from iphone.pair.key.
  • These don't change:
    • Escape means Deny;
    • Allow has no key equivalent;
    • the dialog id stays on DebugDialog.userOnly.

Testing

New boundary tests:

  • PhoneListOwnerTests.theIPhoneHostsAccountDecidesWhoseListItIs: switching team keeps the list, another user gets an empty one, signing out gives none, and switching back restores it.
  • PhoneAdmissionTests.theIrohListenerNeverSetsTheListsUser: stopping the Iroh listener (the team is gone) leaves the list and its user alone.
  • PhoneAdmissionTests.anAllowAnsweredAfterAUserSwitchIsRefusedAndNotSaved.
  • PhoneAdmissionTests.aQuestionOpenAcrossAUserSwitchAdmitsNobody: user-1's question is open, the Mac switches to user-2, and the phone connects again; Allow admits neither connection, and neither list gets the phone.
  • PhoneAdmissionTests.aSameUserListChangeKeepsTheDenyHold and aUserChangeDropsTheDenyHolds.
  • PhoneAdmissionTests.theQuestionShowsTheKeysFingerprint, including a key that isn't hex.

Observed / Not run

Observed:

  • python3 scripts/cmux-next/prepush-gate.py: no new violations.
  • count-swift-code-lines.py --grown 26752c9ced4a: no CmuxNextApp file grew.
  • Fleet compile-check at 2b247fa96846 (job aa36f5c759446d909c5417b3): app passed. CmuxNext failed only on the Linux pool's known no such module 'Testing' infra error, with no other errors.
  • package-test-lane.sh suite at 2b247fa96846, step 1d7a0b86e982bce748f8a1f9 on aws-m4pro-3: all passed. That covers PhoneAdmissionTests (17), PhoneListOwnerTests (1), MobileHostPairingTests and MobileHostMacIdentityTests (11), PhoneAccessGateTests (13), PhoneAccessMigrationTests (4), PhoneAccessSettingTests (5), DebugDialogUserOnlyTests (1) and MobilePageProviderTests (8).

Not run: no live phone pairing over Iroh or hostd. An open question isn't dismissed when the user switches; its answer counts for nobody, and it closes on its own after 120 s.

Changelog

Changed: The Allow question for a new phone shows a short key fingerprint.

Agent session: cosmic-gray

šŸ¤– Generated with Claude Code