From ae951e45e746304878b4fb83aa78b9ce0eb982a4 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sat, 4 Jul 2026 23:50:40 -0700 Subject: [PATCH] Feature Enhancement Planning Nucleic-Session: 3CD3A375-8BC1-407E-951F-8EF136B08390 Co-authored-by: Nucleic --- .../NucleicRemote/Models/HostConnection.swift | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/NucleicRemote/NucleicRemote/Models/HostConnection.swift b/NucleicRemote/NucleicRemote/Models/HostConnection.swift index cfe8b0b..b1d5247 100644 --- a/NucleicRemote/NucleicRemote/Models/HostConnection.swift +++ b/NucleicRemote/NucleicRemote/Models/HostConnection.swift @@ -434,6 +434,29 @@ final class HostConnection { case .peerList(let peers): meshPeers = peers callbacks.didUpdate() + case .meshRoster(let push): + // Mesh "join": the host gossiped its group view. Auto-learn every Mac member into the + // paired-hosts registry (so the multiplexer connects to all of them), and drop any the + // group revoked. Phone members are ignored — a phone doesn't dial other phones. + var changed = false + for member in push.members where member.kind == .mac || member.capabilities.canHost { + // A mac deviceID is `sha256(staticPublicKey)`; reject a forged (id, key) pair. + guard DeviceIdentity.hostID(ofStaticKey: member.staticPublicKey) == member.deviceID + else { continue } + let fingerprint = member.staticPublicKey.fingerprintHex + guard fingerprint != self.hostID else { continue } // that's the Mac we're on + if IdentityStore.pairedHost(id: fingerprint) == nil { changed = true } + IdentityStore.mergePairedHost(Self.pairedHost(from: member)) + } + for tombstone in push.tombstones { + // fingerprint = first 16 hex of the hostID (sha256 prefix), the registry key. + let fingerprint = String(tombstone.deviceID.prefix(16)) + if IdentityStore.pairedHost(id: fingerprint) != nil { + IdentityStore.removePairedHost(id: fingerprint) + changed = true + } + } + if changed { callbacks.meshRosterChanged() } case .transferAccept, .transferReject, .transferReady, .transferCommitted, .transferChunkAck: // Session-transfer replies (mesh P5) only reach a *source* Mac; a phone is never one. break