From c7774fcb6f1e771f61b17852cdbf22e362df9194 Mon Sep 17 00:00:00 2001 From: NIDHAL <88096539+ZitouniNidhal@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:19:40 +0100 Subject: [PATCH 1/3] docs: clarify build tool prerequisites --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a4c13c00..663548fb 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,13 @@ Some features require Multiplex Pro; see [`docs/store-metadata.md`](docs/store-m ## Building -Requires Xcode with the visionOS SDK and [XcodeGen](https://github.com/yonaskolb/XcodeGen). The project is generated, so edit `project.yml`, not the `.xcodeproj`. +Requires Xcode with the visionOS SDK and [XcodeGen](https://github.com/yonaskolb/XcodeGen). Install the command-line prerequisites with [Homebrew](https://brew.sh/): + +```sh +brew install xcodegen swiftlint +``` + +The Xcode project is generated, so edit `project.yml`, not the `.xcodeproj`. Regenerate it after project configuration changes: ```sh xcodegen generate @@ -81,7 +87,7 @@ Point `MULTIPLEX_SEED_HOST` at `Tools/dev-sshd/state/seed.json` to seed a DEBUG ## mpx CLI -[`mpx`](https://github.com/multiplex-term/mpx-cli) is the companion CLI for binding a machine. Run `mpx bind` on the remote host and it appears on the deck with the app's SSH key enrolled and the host key fingerprints pinned from the first connection — nothing typed. The offer reaches the app by QR, local-network announcement with a PIN, or `--copy` to the clipboard; the default handshake never moves a private key. +[`mpx`](https://multiplexterm.dev/mpx/) is the companion CLI for binding a machine. Run `mpx bind` on the remote host and it appears on the deck with the app's SSH key enrolled and the host key fingerprints pinned from the first connection — nothing typed. The offer reaches the app by QR, local-network announcement with a PIN, or `--copy` to the clipboard; the default handshake never moves a private key. Install and usage: [multiplex-term/mpx-cli](https://github.com/multiplex-term/mpx-cli). From 18f5a1c0f56e225f1f760a138c32d12b3b9be993 Mon Sep 17 00:00:00 2001 From: NIDHAL <88096539+ZitouniNidhal@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:20:02 +0100 Subject: [PATCH 2/3] docs: preserve mpx CLI repository link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 663548fb..9977d23e 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Point `MULTIPLEX_SEED_HOST` at `Tools/dev-sshd/state/seed.json` to seed a DEBUG ## mpx CLI -[`mpx`](https://multiplexterm.dev/mpx/) is the companion CLI for binding a machine. Run `mpx bind` on the remote host and it appears on the deck with the app's SSH key enrolled and the host key fingerprints pinned from the first connection — nothing typed. The offer reaches the app by QR, local-network announcement with a PIN, or `--copy` to the clipboard; the default handshake never moves a private key. +[`mpx`](https://github.com/multiplex-term/mpx-cli) is the companion CLI for binding a machine. Run `mpx bind` on the remote host and it appears on the deck with the app's SSH key enrolled and the host key fingerprints pinned from the first connection — nothing typed. The offer reaches the app by QR, local-network announcement with a PIN, or `--copy` to the clipboard; the default handshake never moves a private key. Install and usage: [multiplex-term/mpx-cli](https://github.com/multiplex-term/mpx-cli). From febbf3c4e57f7bf65b65f1d5094e2a479e416f11 Mon Sep 17 00:00:00 2001 From: NIDHAL <88096539+ZitouniNidhal@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:24:39 +0100 Subject: [PATCH 3/3] fix: classify IPv4-mapped IPv6 viewport hosts --- Multiplex/Models/ViewportReach.swift | 16 ++++++++++++++++ MultiplexTests/ViewportReachTests.swift | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/Multiplex/Models/ViewportReach.swift b/Multiplex/Models/ViewportReach.swift index 614087a4..2c4f2d98 100644 --- a/Multiplex/Models/ViewportReach.swift +++ b/Multiplex/Models/ViewportReach.swift @@ -56,6 +56,7 @@ enum ViewportReach: Equatable { if host == "localhost" || host.hasSuffix(".localhost") { return true } if host == "0.0.0.0" || host == "::" || host == "::1" { return true } if let octets = ipv4Octets(host) { return octets[0] == 127 } + if let octets = ipv4MappedOctets(host) { return octets[0] == 127 } return false } @@ -67,6 +68,13 @@ enum ViewportReach: Equatable { if octets[0] == 169, octets[1] == 254 { return true } return false } + if let octets = ipv4MappedOctets(host) { + if octets[0] == 10 { return true } + if octets[0] == 192, octets[1] == 168 { return true } + if octets[0] == 172, (16...31).contains(octets[1]) { return true } + if octets[0] == 169, octets[1] == 254 { return true } + return false + } // IPv6 unique-local (fc00::/7) and link-local (fe80::/10). if host.hasPrefix("fc") || host.hasPrefix("fd") || host.hasPrefix("fe80:") { return host.contains(":") @@ -78,6 +86,14 @@ enum ViewportReach: Equatable { return false } + /// The IPv4 payload of the common `::ffff:w.x.y.z` mapped form, or nil. + private static func ipv4MappedOctets(_ host: String) -> [Int]? { + guard let separator = host.lastIndex(of: ":"), + host[.. [Int]? { diff --git a/MultiplexTests/ViewportReachTests.swift b/MultiplexTests/ViewportReachTests.swift index e91256e2..efabf056 100644 --- a/MultiplexTests/ViewportReachTests.swift +++ b/MultiplexTests/ViewportReachTests.swift @@ -38,6 +38,13 @@ final class ViewportReachTests: XCTestCase { } } + func testIPv4MappedPrivateAddressesKeepTheirIPv4Reach() { + XCTAssertEqual(classify("http://[::ffff:192.168.1.68]:5173/"), .lan) + XCTAssertEqual(classify("http://[::ffff:10.0.0.5]/"), .lan) + XCTAssertEqual(classify("http://[::ffff:127.0.0.1]:8080/"), .remoteLoopback) + XCTAssertEqual(classify("http://[::ffff:8.8.8.8]/"), .internet) + } + func testUnqualifiedSingleLabelIsLAN() { // A bare machine name resolves through the local network's search // domains — LAN by construction.