Releases: mh-mobile/RoamRun
Release list
RoamRun 0.1.19
Keep debugging after the iPhone leaves Wi‑Fi. Upgrading is brew upgrade --cask roamrun.
On cellular the iPhone stops accepting new wireless-debugging connections, but a session Xcode set up on Wi‑Fi can keep working over Tailscale. RoamRun used to end that session itself about three minutes later. Now you choose what happens.
- Settings › Network › Keep debugging on cellular (off by default). On: a device that is Ready for Xcode keeps its session when it moves to cellular, and Run and the debugger go on working. Off: RoamRun closes the session to save data and reconnects once the device is back on Wi‑Fi. The CLI follows the same setting; without the app's Settings, use
defaults write io.github.mh-mobile.roamrun keepDebuggingOnCellular -bool true. - See which network the device is on. The app shows "Ready for Xcode · Wi‑Fi" or "· Cellular", and "Waiting for device · Cellular" while paused.
roamrun statusshows the same, its JSON hasnetwork, androamrun doctorsays "over cellular". - Only from Ready for Xcode. On the Mac's own network ("On this Wi‑Fi"), Xcode reaches the device over the LAN, not through RoamRun, so going from home straight onto cellular still ends the session. To leave home without losing it, join a Wi‑Fi another device makes, such as a travel router or a spare phone sharing its connection; see the README.
- A new session still needs Wi‑Fi, for example after the iPhone restarts or Tailscale drops.
- On cellular, the session uses the iPhone's data: about 2–9 MB an hour at rest, plus each Run.
Checked on an iPhone with Tailscale and with a Manual IP profile:
- Wi‑Fi to 5G with the setting on and off, and back to Wi‑Fi.
- A locked iPhone on Wi‑Fi, which was not taken for cellular.
- Run and a breakpoint on 5G.
RoamRun 0.1.18
Bridges no longer fill up over a long session. Upgrading is brew upgrade --cask roamrun.
While a device is bridged, macOS adds a standby connection to its tunnel about every 40 seconds and never closes them. Toggling the iPhone's Wi‑Fi doesn't close them either. Within an hour a tunnel held 64 connections, and with a few devices bridged, a newly bridged device or a new tunnel could find no room left for its first connection.
- A tunnel now keeps 8 connections. When a new one arrives, RoamRun closes the standby that has been idle longest. It tells standbys from the live tunnel by how much data they have moved, so the connection carrying your debug session stays open even through a quiet spell.
- A relay left behind by an old tunnel is closed once its connections have been silent for 5 minutes.
- The last 16 connection slots are kept for the connection that keeps the device Connected, so full tunnels can't take its place.
- When RoamRun refuses a connection, it now logs why, at most once every 10 minutes per relay.
Checked on an iPhone: the tunnel held at 8 connections and the live one was never closed. The device stayed Connected, and Run and a breakpoint worked from Xcode.
RoamRun 0.1.17
Fixes from a full review of 0.1.16. Upgrading is brew upgrade --cask roamrun.
Bridging
- A device reached only through Tailscale's relays (DERP) no longer reads as down. On hotel or carrier networks that force a relay, RoamRun now finds a device whose address or RemotePairing port moved, and
roamrun doctorstops telling you to unlock a device that is simply relayed. - Two devices bridged at once no longer take each other's tunnel ports (#26). A device's tunnel now goes to its own bridge, whether both are bridged from the app or one from the app and one from
roamrun up. - The app no longer takes a device from a running
roamrun up. Its automatic restarts — at launch, after an error, after a network change, when the device leaves this Wi‑Fi — leave a deviceroamrun upis handling alone, and pick it back up once that ends. Pressing Start still takes over an errored or standing-aside one. - A bridge stopped or restarted while it was looking for a device no longer comes back as "Starting…" for good.
- A relay that stops listening is noticed and restarted instead of leaving the bridge looking alive.
Over the air
- The install page answers only under this Mac's tailnet name, so a web page on this Mac can't read your builds through
127.0.0.1. - A stray file in the builds folder no longer takes the page down, and quitting RoamRun while it tidies up at launch still gives the port back.
roamrun doctorreports missing HTTPS certificates as a warning; it fails only when Funnel has put the page on the internet.
Also
- A device list that can't be read is never written over — not at launch, and not by the first save after it becomes readable again.
- "Open at login" shows off, with the reason, when this copy can't turn it back on by itself.
- Find RemotePairing Port no longer leaves a running bridge off.
roamrun statusexplains why a device "On this Wi‑Fi" isn't ready;--jsonreports unknown values asnull.roamrun installchecks the signing of the very app it installs, and cleans up after an interrupted install.
Checked on an iPhone on another network: bridging, launch and a breakpoint from Xcode; moving onto this Mac's Wi‑Fi and back; roamrun up alongside the app; Mac sleep; and an over-the-air install, with quit, force-quit and reclaim of the page's tailscale serve entry.
RoamRun 0.1.16
Install a build on a device without the bridge. Upgrading is brew upgrade --cask roamrun.
roamrun ota [<name>] <App.ipa> [--replace]
Stores a signed .ipa and, while RoamRun runs, publishes an install page on a port of its own through tailscale serve. Open the address it prints on the device — or point the camera at the QR code — and tap Install. No bridge, so no Wi-Fi requirement and no pairing with this Mac; the page is served over the tailnet and the device fetches it over Wi-Fi or cellular alike.
It installs only. No debugger, no roamrun logs, no roamrun screenshot — all of those need the bridge. Use it to try a build, not to work on one.
What it needs
- A paid Apple Developer account, and an .ipa signed for Release Testing (Ad Hoc) or Enterprise.
roamrun runcan't produce one: it signs for Development, which only installs through the bridge, androamrun otarefuses it rather than letting iOS fail with nothing to go on. - MagicDNS and HTTPS certificates on for your tailnet. Without certificates
tailscale servewrites nothing and still exits 0, soroamrun otaandroamrun doctorname that outright rather than telling you to wait. - RoamRun running, since it is the app that serves the page.
Anyone on your tailnet can open that page and install those builds. On a tailnet you share, restrict it with Tailscale Grants / ACLs.
What it does with your tailscale serve
A port of its own — 41443 by default, defaults write io.github.mh-mobile.roamrun otaPort -int … to change it — and never a path on your tailnet's :443, where whatever else you serve lives. Funnel can only publish 443, 8443 and 10000, so a port outside those three can't be put on the internet; RoamRun never turns Funnel on and says so loudly if it finds it on anyway.
It only ever replaces or removes an entry it can prove it made, records the exact port and address it registered, and gives the port back when it quits. A registration left by a run that was killed is recognised and released at the next launch.
Also
- An Ad Hoc build needs Developer Mode on the device to launch on iOS 16 and later. No Mac is involved: the switch appears under Settings › Privacy & Security once the app is installed. An Enterprise build needs its developer trusted instead. Both READMEs and the install page say so.
- The page keeps the last 5 builds of each app, newest first, so you can go back a version. Expired provisioning is marked and offers no Install button.
roamrun otasays which of your devices the build covers. A build covering none of them is stored with a warning rather than refused — the profile may name a device this Mac has never seen.
Ten rounds of review, three of them by outside reviewers, and a device-side pass on an iPhone and an iPad.
RoamRun 0.1.15
Four reviews of 0.1.14, then a pass that verified the findings and threw out four of them. Upgrading is brew upgrade --cask roamrun.
RoamRun now tells you when the problem is this Mac
- Tailscale signed out or disconnected used to read as "Tailscale is running (0 peers)", and the next line told you to sign your iPhone into the tailnet. It now says which it is, and what to do on this Mac.
- Add Device listed reasons your device might not be showing up — the moment you opened it, while RoamRun itself was still looking. The hints wait a few seconds now, and they mention the Local Network permission, which is the one cause that leaves the list empty with nothing else to see.
- A bridge started with
roamrun upfinds a blocked local network in its own process, so the app's window never heard about it. It does now.
Fixed
- A device on your network could stop a bridge. RoamRun reads
remotepairingd's log, and it assumed one line was one message. It isn't: a newline inside a message is printed as a real newline, so a Bonjour name containing one could split a record and leave a chosen fragment on a line that still looked genuine. That was enough to fake "this Mac doesn't recognise the pairing", which stops the bridge and asks you to remove the device. Log records are now read in a format that can't be split this way. - After
roamrun upsaved a device's new address, the app's running bridge kept the old one and spent a retry rediscovering it. - A relay that can't take a port now says another device's bridge may already hold it, which is usually what happened. Two devices whose tunnel ports land in the same range still collide — this only makes it legible.
Docs
- The website offered "your phone's tethering" as a place the device can be. That is the one arrangement that can't work: the device has to be a Wi‑Fi client itself, so it has to be someone else's hotspot.
roamrun up -dwaits up to 60 seconds and exits 1 if the bridge isn't ready yet (it keeps trying). Only the agent skill said so;--helpand the READMEs do now.- A Getting help section with what to put in an issue, and a link to the release notes.
- SECURITY.md said RoamRun "only relays bytes". It also probes, handshakes and scans for a port — all to your own device. The accurate claim is the narrower one: nothing is sent to us or anyone else, and there is no telemetry and no update check.
RoamRun 0.1.14
Fixes, and one thing RoamRun can now tell you about. Upgrading is brew upgrade --cask roamrun.
RoamRun now says when macOS is blocking its local network access (#23)
- When that permission is off, every check RoamRun makes on your own Wi‑Fi fails at once, so it decides the device is away and goes on bridging — and advertising — a device sitting right next to it. Everything over the mesh VPN keeps working, so nothing else looks wrong and there was no way to tell.
- RoamRun now recognises it and says so in the window, the activity log,
roamrun statusandroamrun doctor, with what to do about it. The bridge is left running: away from home it still works, and someone who is out can't reinstall the app.
Fixed
- Your device list could lose what
roamrun uphad saved. The app reads the list once when it starts and keeps it while it runs, so saving a rename later wrote that older copy back — undoing the address or port a bridge had learned in the meantime, and in the worst order losing a device added from the other side. Saves now re-read the file under a lock and keep both sides' changes. roamrun status --wait Ncould block far longer than N. Each round runs twodevicectlcalls per device before the deadline is looked at again, so--wait 1could sit for twenty seconds. Each call now gets only the time the wait has left. (A very large--waitalso crashed.)- Stopping a bridge could signal the wrong process. The owner was remembered by process id alone, so once macOS had reused that id, an unrelated process could be asked to quit. It is checked again first.
Docs
- What to do if the Local Network permission is stuck, and why
--zapneeds care: it deletes your saved devices, so putprofiles.jsonback before opening RoamRun again. - The release checklist covers revoking local network access.
RoamRun 0.1.13
Mostly fixes and polish. Upgrading is brew upgrade --cask roamrun.
Fixed
- Open at Login could quietly turn itself off. Replacing the app — or the bundle id change in 0.1.12 — dropped the registration, and RoamRun showed the switch as off. Your choice is now remembered and the registration is restored.
- A bridge could hand itself to the wrong process. The owner of a bridge was recognised by its process id alone, so once that id was reused, another RoamRun could be mistaken for it. The owner's start time is now checked too.
roamrun downnow also stops a RoamRun from before 0.1.12.
Better with many devices, and with VoiceOver
- The sidebar, the Add Device list and the menu scroll instead of running off the screen, and long device names truncate instead of pushing buttons away — noticeable on a network with a lot of devices.
- Buttons, the chosen device in Add Device, the connection line and the menu bar icon now read properly in VoiceOver, and the status icon doesn't spin with Reduce Motion on.
Docs
roamrun statuswithout a device name lists every saved device and exits 0 if any of them is ready — say the device's name when a script needs the answer to be about that one.--waitis judged between rounds, so it can return a little past N. Both are now in--helpand the README.roamrun screenshotis confirmed on Xcode 26.3 and later; earlier versions are untested.- The README covers putting a file or the clipboard on the device through the bridge, and SECURITY.md notes that a device added before its UDID was known could learn the wrong one from the network.
- There's a website now: https://mh-mobile.github.io/RoamRun/
RoamRun 0.1.12
- Notarized by Apple: signed with a Developer ID and notarized (app and dmg are stapled), so RoamRun opens like any other app — no more "Open Anyway".
- New bundle id
io.github.mh-mobile.roamrun(wascom.roamrun.app, a domain that isn't ours). Settings and the bridges you had running carry over automatically on first launch, and a running older RoamRun is asked to quit. Because macOS sees a new app:- turn Open at login on again in Settings (remove a leftover "RoamRun" entry in System Settings › General › Login Items if one stays),
- allow Local Network access again if asked,
- the log subsystem is now
io.github.mh-mobile.roamrun.
- Agent skill: stops unless the device is really ready (a failed status, a missing UDID or an unknown lock state), leaves bridges running unless you ask to stop them, and explains which Wi‑Fi works away from home.
roamrun status/doctorsay when an installed skill is from another version — runroamrun initto update it. - Add Device: the device list scrolls on a busy network instead of growing off the screen.
RoamRun 0.1.11
- Launch options for
roamrun runandroamrun logs:--arg A(repeatable, may start with-),--env NAME=valueand--url URL— e.g. open one screen of the app, thenroamrun screenshotit. - Agent skill refocused: it now covers getting the device connected (and screenshots of it), and leaves building and launching to your usual tools or other skills, so it doesn't compete with them. It also explains how to reach a screen and relaunch without rebuilding. Run
roamrun initagain to update an installed skill. - One bridge per device, also while starting: a bridge that is still starting steps back within ~10 s if another RoamRun took the device (e.g. after
status.jsonwas deleted), and checks again right before advertising. - Add Device scan: lines from a stopped scan no longer bring back old results.
Not notarized: on first launch, allow it once in System Settings → Privacy & Security → Open Anyway. After replacing the app with a newer dmg you need to do this again; after brew upgrade you don't.
RoamRun 0.1.10
- Version in the app: shown in Settings and in the menu bar menu.
- One bridge per device, even when things go wrong: if
status.jsonis deleted while a bridge runs and another RoamRun then claims the device, the first one notices within ~10 s and steps back. Two saved profiles for the same iPhone (same UDID) can't both bridge it. - Swift 6 language mode: data-race checks are compile errors in every build; CI also fails on any warning in the app, the tests and the screenshot build.
Not notarized: on first launch, allow it once in System Settings → Privacy & Security → Open Anyway. After replacing the app with a newer dmg you need to do this again; after brew upgrade you don't.