At 6:51 PM, the direct SSH action in iOS Shortcuts was blocked before it could create a single desktop session. That was frustrating for about ten minutes. Then it made the design clearer.
We wanted one tap on our phone to start a fresh remote-control session on our PC and return a join URL. The PC already had the right runtime, project context and working directory. The phone didn’t need to become a development machine. It needed one safe, narrow way to ask the PC to do a specific job.
The first implementation was rc_spawn.sh. It starts a fresh remote-control session and hands back the join URL. We verified that flow live twice. It was the smallest useful primitive, with no terminal proxy and no generic command runner. One command in, one session out.
The transport was the problem. We expected iOS Shortcuts to make the SSH call, and it didn’t. The block was on the phone side, at the app layer. The script was good and the remote host was reachable. The shortcut simply couldn’t use the SSH path we needed. That was the wrong turn: we spent our first effort pushing on Shortcuts, because a native iOS action would have made the demo cleaner, and the limitation sat in a part we couldn’t control.
The thing that looked broken wasn’t broken
This wasn’t the first false diagnosis in the mobile session work.
Earlier, we checked whether sessions were dying because of crashes or dropped SSH connections. The evidence pointed somewhere less exotic: PC reboots and Cursor restarts. The remote-control service wasn’t silently failing. Its host process was disappearing underneath it.
That changed the fix. A stronger SSH retry loop would not survive a reboot. More terminal plumbing would not preserve a session when the editor restarted. So we used Claude Code Remote Control as the durable handoff point. /rc can lift a running session into the Claude app with its full history, and a new PC-hosted session can be spawned from the phone when there’s no session to resume. We verified both paths live from iOS.
We also found a relay problem that was easy to misread. The phone session wasn’t actually running inside the relay. Once we proved that, we fixed the rendering properly: 256-color output, a phone-sized resize and a visible relay bar. The goal was a definitive in-or-out test. When a session claims it’s in the relay, the phone has to make that claim falsifiable.
The session failures and the rendering confusion pointed the same way. Don’t add automation until you can name the layer that failed.
Moving the boundary to SSH
Shortcuts was a dead end for direct SSH, but SSH itself was fine. That led to a more useful pattern: move the automation boundary from the client application to the server protocol.
Instead of granting the phone a general shell and trying to reproduce desktop scripting on iOS, we wired a Termius identity to a forced-command SSH key. That key doesn’t exist to open a terminal. Its only job is to invoke rc_spawn.sh on connection.
The sequence is deliberately narrow:
- The phone connects with the dedicated Termius identity.
- The server accepts the SSH key and runs its forced command.
- The forced command invokes
rc_spawn.sh. - The script starts a fresh remote-control session on the PC.
- The connection returns the join URL to the phone.
The phone asks for a session. The PC creates one where the working environment already lives.
Broader mobile shell access was the other route. We passed on it because it widened the security boundary. A mobile SSH key that can run arbitrary commands is a remote terminal with a small screen. A forced-command key is an API with one endpoint.
With a forced command, the identity is scoped to exactly one capability: create a remote-control session. It needs no access to the rest of the machine’s command surface to be useful.
When we documented this, the server side was wired and tested. The remaining step was configuring the Termius identity on the phone, which is a setup task.
Each part does what its platform supports
The join URL is the visible payoff. The phone can’t run the client-side automation we first reached for, but it can speak SSH. The PC can enforce a forced command. rc_spawn.sh can create the session.
When an automation is blocked in a particular app, we find the open protocol beneath it, shrink the capability to the one action we need, and let the server own the privileged work. One tap is just the interface. The desktop session spawner is now a constrained protocol call instead of a fragile phone script.
If a phone automation tool blocks a direct connection, give the phone a key that can do exactly one job and let the server protocol carry it.