All writing

Building From My Phone: Claude Code, caffeinate, and an ngrok Tunnel

I can start a UI change on my laptop, walk out the door, and watch the result render on my phone — tweaking it from a coffee shop while the work happens back home. It sounds like it must involve the cloud. It doesn't, and that's the whole trick: getting one fact right about where the work runs is what makes the rest of the setup fall into place.

The fact everyone gets backwards

When you turn on /remote-control and start sending messages from your phone, the natural assumption is that "remote" means the cloud — that your task has been shipped off to a server somewhere and is running there. So people brace for the usual cloud tradeoffs: my local tools won't be there, my environment won't be there, my files won't be there.

That assumption is exactly backwards. Remote Control does the opposite of what the name suggests. In Claude Code's own words, it "allows you to control sessions on your local device from" elsewhere, running "as a persistent server" on your machine. The session lives on your laptop. Your phone is a remote — it sends the keystrokes, but your Mac is the thing doing the work, with full access to your real filesystem, your installed CLIs, your skills, your environment. Nothing was shipped anywhere.

Don't confuse it with cloud agents

This is a different feature from a cloud routine, which really does run in an off-machine sandbox and really does lose your local environment — I wrote about when to reach for that in Local or Remote. Remote Control is the inverse: same local session, just steered from your pocket.

Once that clicks, the entire mobile workflow reorganizes around it. If the work runs on my Mac, then two very physical problems appear — and both have clean fixes.

Problem 1: a sleeping Mac kills the session

The session is a process on a laptop. Laptops sleep. Close the lid or let it idle and the process freezes mid-task — the worst possible moment, because you're not there to notice. The fix is a one-word macOS built-in: caffeinate, which holds a power-management assertion so the machine stays awake.

The flags map to the kinds of sleep you're blocking:

caffeinate [-i] [-d] [-m] [-s] [-t timeout] [-w pid] [command...]
  -i  prevent idle sleep      -d  prevent display sleep
  -m  prevent disk sleep      -s  prevent system sleep (on AC power)
  -t  hold for N seconds      -w  hold until a given PID exits

The clean pattern is to wrap the session, so the assertion lives exactly as long as the work does and releases the moment it ends:

caffeinate -i -s claude     # no idle/system sleep until this session exits

No daemon to remember to turn off, no setting left flipped — when claude exits, the Mac is free to sleep again.

Problem 2: your dev server is invisible to your phone

Now the interesting one. You ask Claude to build a UI change and spin up the dev server. It does — on http://localhost:5173, on your Mac. That URL means nothing on your phone; localhost is the laptop, and your phone is on a different network entirely. The preview you want to see is running on a machine you're not sitting at.

This is what a tunnel is for. ngrok opens a public HTTPS endpoint and forwards it to a port on your machine, so a request from anywhere on the internet lands on your local dev server. I ask Claude to start it right alongside the server:

$ ngrok http 5173

Session Status   online
Forwarding       https://tallowy-pachydermous-breana.ngrok-free.dev -> http://localhost:5173

That forwarding line is real — I ran exactly this against a throwaway page while writing this, and a request to the public URL came back 200, serving the page off my laptop. Claude pastes that URL into the session, I open it on my phone, and I'm looking at the live app. Every change Claude makes that the dev server hot-reloads shows up on my screen seconds later. I can sit on a train and say "tighten that spacing, the CTA's too low" and watch it move.

Why this only works because of fact #1

The tunnel is necessary precisely because the server is local. If Remote Control really were cloud execution, there'd be nothing on your Mac to tunnel to. The ngrok step isn't a workaround for the cloud — it's the bridge to a server that never left your desk.

Review intent, not just pixels

A phone is a great window for seeing a result and a terrible one for reviewing a large diff. So I shift what I review. Before Claude writes much code, I have it put the plan — the component breakdown, the decisions, the file list — into a Markdown file and show me that first. Approving a short, structured plan on a small screen is easy; scrolling a 600-line diff on a phone is not.

It also gives me a clean handoff. The genuinely heavy steps — a big migration, a full test run, anything I'd rather watch on a real terminal — can wait. The plan is committed, the intent is reviewed, and I run those parts when I'm back at the desk. The phone is for steering and previewing; the terminal is still there for the work that wants a keyboard.

The part you can't be casual about

A tunnel is a hole in your machine, punched out to the open internet. Treat it like one.

  • Only ever tunnel a dev server, never anything sitting on real data or secrets. The public URL is reachable by anyone who has it.
  • Put auth on it if it's up for more than a moment — ngrok can add basic auth or OAuth in front of the tunnel. A guessable URL is not privacy.
  • Close it when you're done. The tunnel should live as long as the session, not as a thing you forget is running. (ngrok's free tier also shows a browser interstitial on first visit, a small friction that's easy to mistake for a broken link.)

The loop

Put together, building untethered comes down to four moves, each one a direct consequence of the work living on your own machine:

  • Keep the Mac awake for the whole session: caffeinate -i -s claude.
  • Turn on /remote-control and drive the local session from your phone.
  • Have Claude tunnel the dev server with ngrok http <port> and paste you the URL for a live preview.
  • Review the plan, not the diff, on the small screen — and leave the heavy steps for the terminal.

None of it is cloud magic. It's a local session you can reach, a machine you've told to stay awake, and a bridge to a server that never left home. The surprise isn't how exotic the setup is — it's how ordinary the pieces are once you stop assuming "remote" means "somewhere else."

Written by Pavel Kerbel — Software Engineer. More writing