Skip to content

Instantly share code, notes, and snippets.

@fredchu
Created October 15, 2026 05:10
Show Gist options
  • Select an option

  • Save fredchu/7529719e1f79345a4f0d4bb55462e48d to your computer and use it in GitHub Desktop.

Select an option

Save fredchu/7529719e1f79345a4f0d4bb55462e48d to your computer and use it in GitHub Desktop.
Pairing an Apple Vision Pro for Madeira JIT from a Mac (host-initiated RPPairing via idevice)
// Host-initiated remote pairing (the Xcode direction) with a device showing
// Settings > General > Remote Devices, e.g. visionOS. Writes the RPPairing plist.
use idevice::remote_pairing::{RemotePairingClient, RpPairingFile, RpPairingSocket};
const PIN_FILE: &str = "/tmp/vp-pin.txt";
#[tokio::main(flavor = "current_thread")]
async fn main() {
let mut a = std::env::args().skip(1);
let addr = a.next().expect("usage: pair_host <host:port> <out.plist>");
let out = a.next().expect("usage: pair_host <host:port> <out.plist>");
let _ = std::fs::remove_file(PIN_FILE);
let stream = tokio::net::TcpStream::connect(&addr).await.expect("connect");
eprintln!("connected to {addr}");
let mut file = RpPairingFile::generate("Madeira on Mac");
let mut client = RemotePairingClient::new(RpPairingSocket::new(stream), "Madeira on Mac");
let res = client
.connect(&mut file, || async {
eprintln!("WAITING_FOR_PIN (write it to {PIN_FILE})");
loop {
if let Ok(p) = std::fs::read_to_string(PIN_FILE) {
let p = p.trim().to_string();
if !p.is_empty() { eprintln!("got pin"); return p; }
}
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
})
.await;
match res {
Ok(()) => {
std::fs::write(&out, file.to_bytes()).expect("write");
eprintln!("PAIRED, wrote {out}");
}
Err(e) => { eprintln!("FAILED {e:?}"); std::process::exit(1); }
}
}
#!/bin/bash
# Pair a Mac with an Apple Vision Pro that shows Settings > General > Remote Devices,
# producing an RPPairing file for Madeira / StikDebug.
# pair_host is build/rppairing-ios with crate-type "rlib" and pair_host.rs added as a bin
# (tokio feature "fs"). macOS Local Network privacy blocks unsigned binaries from reaching
# the headset ("No route to host"), so the connection goes through /usr/bin/nc.
# Usage: ./vp-pair.sh <vision-pro-ipv4> ; then write the PIN shown on the headset to /tmp/vp-pin.txt
VP_IP=$1
P=$(script -q /dev/null sh -c 'dns-sd -Z _remotepairing-manual-pairing._tcp local. & sleep 4; kill $!' | grep -a SRV | grep -v '^;' | head -1 | awk '{print $5}')
[ -z "$P" ] && { echo "Vision Pro is not on Remote Devices"; exit 1; }
rm -f /tmp/vp-relay.fifo; mkfifo /tmp/vp-relay.fifo
(nc -l 127.0.0.1 40125 < /tmp/vp-relay.fifo | nc "$VP_IP" "$P" > /tmp/vp-relay.fifo) &
sleep 1
./pair_host 127.0.0.1:40125 visionpro-pairing.plist
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment