~/abhipraya
Coding From My iPhone Without Coding On It
I do real development work from my iPhone, but the phone does none of it. The agents run on my MacBook or my VPS in long-lived terminal sessions. The phone connects into those sessions, I say what I want, and the agent does the typing.
Once you see the phone as a remote control instead of a small laptop, the setup comes down to four problems: reaching the box safely, surviving bad networks, not losing your session, and not typing long prompts on glass.
The Stack
- Tailscale so the server’s shell is reachable only over a private network
- Blink Shell as the terminal (about 20 dollars a year)
- Mosh so the connection survives network changes
tmuxon the remote machine so sessions outlive the connection- Wispr Flow for speech-to-text
- Claude Code and Codex as the agents, sharing the same
~/.agentsinstructions and skills described in the MacBook post
graph TD
Phone["iPhone
Blink Shell + Wispr Flow"]
Phone --> Tailnet["Tailscale tailnet"]
Tailnet --> Mosh["Mosh session"]
Mosh --> Tmux["tmux session"]
Tmux --> Claude["Claude Code"]
Tmux --> Codex["Codex"]
Claude --> Shared["~/.agents
instructions, skills, MCP"]
Codex --> Shared
No Public SSH at All
My VPS does not accept SSH from the internet. The firewall drops port 22 from everywhere except Tailscale, which puts my MacBook, VPS, iPhone, and laptops on one private network.
Compared with the usual “open port 22 with key auth and hope”, there is no SSH port for anyone to scan or brute-force, and I never need a jump host or an IP allowlist for my own devices. From the phone, connecting is just mosh vps.
One trap to check on your own box if you run Docker: a published port like -p 8080:80 (or "8080:80" in a compose file) binds to 0.0.0.0, and Docker writes its own iptables rules that skip UFW entirely. ufw status can show the port as closed while it is open to the whole internet. Test from outside the server, for example nc -z <public-ip> 8080 from your phone’s mobile data, instead of trusting the firewall’s own report. If nginx sits in front of the container, publish it as "127.0.0.1:8080:80" so only nginx on the same machine can reach it.

Source: tailscale.com
Plan Your Way Back In First
The catch with tailnet-only access: if Tailscale on the server logs out or breaks, you have also closed your only door. Before you close port 22, find your out-of-band access and test it once while everything still works.
For most VPS providers that is a VNC or serial console in the web panel. Mine (Jetorbit) has one, and the recovery looks like this:
- Enable VNC in the provider panel and open the console.
- Press Enter to get a login prompt. It may be buried under firewall log spam; switching to another TTY with
Ctrl+Alt+F2helps. - Log in as root, or as your normal user if you know its sudo password. If you do not know the root password, many providers can reset it from the panel without touching your data. (As root, drop the
sudoin the next steps.) - Silence kernel messages so you can actually type:
sudo dmesg -D. - Run
sudo tailscale up, open the printed URL on another device, and authenticate. - Turn kernel messages back on with
sudo dmesg -E, and disable VNC in the panel.
Write this down somewhere you can reach without the server, like a note on your phone. The time you need it is exactly when you cannot SSH in to read it. Also check that key expiry is turned off for the server in the Tailscale admin console (it is on mine). Otherwise its login quietly expires months later and you are back at step one.
Mosh Keeps the Session Alive
On a phone, the network changes constantly: Wi-Fi to mobile data, an elevator with no signal, iOS suspending the app. Plain SSH runs over one TCP connection and dies when that connection breaks. Mosh runs over UDP, is not tied to one connection, and resyncs when the network comes back. I can lock the phone, walk somewhere else, and pick up the same session ten minutes later.
Blink Shell is the paid part of the setup, and Mosh support is most of why I pay for it. Its custom keyboard bar is the rest. Agent and tmux work need escape, control, arrows, tab, and pipe, which iOS buries. My rule is that any key I use more than twice in a session gets a slot on the bar.

Blink Shell with the custom keyboard bar, reconnected into an agent session that kept running while the phone was locked.
tmux Makes Disconnecting Free
Mosh survives network changes, but if the client really goes away, the session needs to live on the server. Logging in over SSH or Mosh opens a small picker I wrote, tmux-menu, built on fzf and a few lines in ~/.bashrc. I pick a session and land back where I was, with the agent’s context intact. Esc drops me to a plain shell when I just need one command.

tmux-menu is the first thing I see when I log into the VPS. Pick a session, jump back into it.
This matters more on a phone than anywhere else. If disconnecting means starting over, you will not start anything from your phone. If it is free, you will.
If you want a native app instead of a terminal, Claude Remote Control (Claude Code only) and T3 Code’s mobile app (Claude Code, Codex, and others) are simpler paths, with native notifications. I stay with the terminal route because the same tmux session also works from my MacBook, and it covers Codex too.
Say the Intent, Do Not Type Code
Typing long prompts on an iPhone keyboard is the worst part of this, so I mostly do not. Wispr Flow installs as a keyboard you tap to speak into. I say a messy instruction, clean it up a little, and send it.

Wispr Flow as the iPhone keyboard. Speaking a paragraph is much faster than thumb-typing it.
What you dictate should be intent, not code. For example: “Check the failing CI, inspect the relevant test, fix it if the root cause is obvious, and do not push without showing me the diff.” That is painful to type on a phone and takes five seconds to say. It also sets the limits up front (fix only if obvious, show the diff first), which matters when you will not be watching every step.
What the Phone Is Good and Bad At
Good for short interruptions where the agent does the work:
- asking an agent to look at a failing PR while I am away from my desk
- approving a small fix after reading a short diff
- checking whether a long command or agent run finished
- creating a task or issue from the context of a session
- sending a spoken instruction into a session that is already running
Bad for anything where I need to read a lot: deep code review, large diffs, designing a feature. I can technically do those on the phone, but the screen is too small to judge the work well, so I leave them for the MacBook.
Getting Started
If you want a version of this:
- Put the box on Tailscale, then test your provider’s console recovery before you close public SSH.
- Connect with Mosh instead of SSH, from a terminal app that supports it.
- Run everything inside tmux, so a disconnect never kills an agent.
- Use voice input for prompts, and include the limits in what you say.
Once those are in place, the phone only has to be good at giving direction.