Completes the multiplexer rewire begun in f335eb4. RemoteStore no longer runs an inline single connection — it owns `connections: [HostID: HostConnection]` and connects every paired Mac at once, so all your Macs are live simultaneously. - reconnect() connects/reconnects all paired hosts (dropping since-unpaired ones); pair() adds a new connection and makes it active while the others keep running; unpair() drops the active Mac and switches to a remaining one. - The flat @Published state (connectivity/sessions/dashboard/capabilities/…) mirrors the *active* connection via callbacks, so switchHost() is now instant — every Mac is already connected, so it just re-points at the target's live state (no reconnect). - Aggregate concerns merge across hosts: the app-icon badge counts needs-you across ALL Macs, Live Activity summarizes all live sessions, and an approval answered from a notification is broadcast to every connection (the owner resolves it; the notification carries no hostID yet). Intents (send / open / approvals) route to the active connection. Removed ~450 lines of connection machine from RemoteStore (now in HostConnection); kept friendlyTransportError/showError/dismissError. Demo bypasses connections (seeds state directly). Compiles (iOS Simulator BUILD SUCCEEDED) and the demo host switcher is runtime-verified (screenshots: switching Macs swaps the session list + tab badge). The LIVE multi-connection path — simultaneous dials, reconnect/teardown, the shared embedded tailnet node — is compile-verified only; it needs two real Macs to validate. Co-Authored-By: Claude Opus 4.8 <[email protected]>
NucleicRemote (iPhone client)
The thin iOS remote client for Nucleic (PLAN milestone M4). It's a pure projection of the
Mac host over LAN: monitor sessions, read transcripts/diffs, answer approvals, and send
follow-up input — scope approve. No local git or CLI; the Mac is the single authority
(see docs/UX_IOS.md and docs/SYNC_PROTOCOL.md).
Architecture
All wire/crypto logic is shared with the Mac via the NucleicProtocol SwiftPM library
(this Xcode project links it as a local package at ../..):
- Transport —
NWFrameChannel(NWConnection) +LANDiscovery(Bonjour_nucleic._tcp). - Engine —
NucleicProtocol.SyncClientruns the Noise handshake (XXpsk0 to pair, IK to reconnect), exchanges hello/welcome, and turnsHostMsgs into aSyncClient.Eventstream. - State —
RemoteStore(ObservableObject) is the single on-device UI state, a pure projection of the host. Identity + pinned host live inIdentityStore(Keychain + UserDefaults). - UI — SwiftUI:
SessionsView(attention-first list),SessionDetailView(transcript/diff + status-driven action area),ApprovalCardView(Face ID gate on high-risk),PairingScannerView(QR),SettingsView.
Build & run
# Resolves the local NucleicProtocol package automatically.
xcodebuild -project ios/NucleicRemote/NucleicRemote.xcodeproj \
-scheme NucleicRemote \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro' build
Or open NucleicRemote.xcodeproj in Xcode and run. To pair, start the sync server on the Mac
(Nucleic ▸ Settings ▸ Add iPhone shows the QR), then scan it. On a real device, both must be
on the same Wi‑Fi.
Status
The full pair → list → subscribe → approve → reconnect path is implemented and the protocol/
server side is covered by tests in Tests/NucleicProtocolTests and Tests/NucleicCoreTests.
Push notifications / Live Activity (UX_IOS §5.1/§5.3) are the M5 follow-up (needs the relay).