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 clarified the design.

I wanted one tap on my phone to start a fresh remote-control session on my PC and return a join URL. The PC already had the right runtime, project context, and working directory. The phone did not 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. I verified that flow live twice. It was the smallest useful primitive: not a terminal proxy, not a mobile IDE, and not a generic command runner. One command in, one session out.

The problem was the transport. I expected iOS Shortcuts to make the SSH call. It did not. The block was on the phone side, at the app layer. The script was good. The remote host was reachable. The shortcut simply could not use the SSH path I needed.

The thing that looked broken was not broken

This was not the first false diagnosis in the mobile session work.

Earlier, I 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 was not silently failing. Its host process was disappearing underneath it.

That distinction 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. Instead, 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 is 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 was not 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 not polish. It was a definitive in-or-out test. When a session claims it is in the relay, the phone needs to make that claim falsifiable.

The session failures and rendering confusion made the same point. Do not add automation until you can name the layer that failed.

Moving the boundary to SSH

iOS Shortcuts was a dead end for direct SSH, but SSH itself was not the problem. That led to the 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, I wired a Termius identity to a forced-command SSH key. That key does not exist to open a terminal. Its only job is to invoke rc_spawn.sh on connection.

The sequence is deliberately narrow:

  1. The phone connects with the dedicated Termius identity.
  2. The server accepts the SSH key and runs its forced command.
  3. The forced command invokes rc_spawn.sh.
  4. The script starts a fresh remote-control session on the PC.
  5. 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.

I considered two alternatives. The first was to keep pushing on Shortcuts, because a native iOS action would have made the demo cleaner. It lost because the phone-side SSH limitation was outside the part I could control. The second was broader mobile shell access. It lost because it solved the wrong problem and 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.

That distinction matters more than convenience. With a forced command, the identity can be scoped to exactly one capability: create a remote-control session. It does not need access to the rest of the machine’s command surface to be useful.

At the point I documented this, the server side was wired and tested. The remaining step was configuring the Termius identity on the phone. That is a setup task, not an architectural uncertainty.

One tap is not the interesting part

The join URL is the visible payoff, but it is not the real win. The phone cannot run the client-side automation I first reached for. It can speak SSH. The PC can enforce a forced command. rc_spawn.sh can create the session. Each component is doing the job its platform supports.

When an automation is blocked in a particular app, I do not want to fight the app forever. I want to find the open protocol beneath it, shrink the capability to the one action I 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, not a fragile phone script.