Get Moshi
simulator

Simulator preview

Open a booted iOS Simulator from your Mac inside Moshi, streamed by serve-sim and forwarded over SSH. Touch it live, no public tunnel.

updated 1 week ago4 min readpage 32 / 41

When an agent is working on a native app, the thing you want to look at is the Simulator, not a log. serve-sim turns any booted iOS Simulator on your Mac into a web page with a live video stream and touch control. moshi-hook recognizes that page, and Moshi opens it in-app over the same SSH session you already use for the terminal.

Simulator previews ride on the same host gateway as Browser preview. If browser preview works on a host, simulator preview needs only serve-sim on top.

Requirements

  • A Mac with Apple silicon, Xcode command line tools (xcrun simctl), and a booted iOS Simulator.
  • Node.js 20 or newer on the Mac.
  • moshi-hook 0.2.68 or newer installed and running on the Mac. See Hooks.
  • Moshi 3.11 or newer, connected to the Mac over SSH or Mosh.
  • A Pro subscription. Simulator preview is part of the host gateway, together with browser preview and the diff viewer.

Install serve-sim

serve-sim started as Evan Bacon's personal project and is now maintained by Expo as a workspace in the expo/serve-sim repository. The package moved with it, so install the Expo-scoped package rather than the old unscoped serve-sim:

npx @expo/serve-sim

That command auto-detects the booted Simulator, starts the streaming helper, and serves the preview page on port 3200. Useful variants:

npx @expo/serve-sim "iPhone 17 Pro"   # target a specific device
npx @expo/serve-sim --detach          # run in the background and return JSON
npx @expo/serve-sim --list            # show running previews
npx @expo/serve-sim --kill            # stop every helper
npx @expo/serve-sim -p 4000           # change the starting port

bunx @expo/serve-sim works the same way if you prefer Bun. The unscoped serve-sim package on npm is the original, no longer updated release. It still streams, but new features and fixes land only in @expo/serve-sim, so switch existing scripts and agent skills to the scoped name.

If you want your coding agent to drive the Simulator itself, serve-sim also ships as an agent skill:

bunx add-skill expo/serve-sim

Nothing about serve-sim needs to know about Moshi. Run it however you normally would, and moshi-hook picks it up.

How Moshi finds it

moshi-hook probes local HTTP listeners the same way it does for dev servers. A listener is classified as a simulator preview, not a plain web server, when any of these match:

  • The process or its command line contains serve-sim.
  • The state file serve-sim writes under $TMPDIR/serve-sim/ lists the port. This covers the CLI and the @expo/serve-sim/middleware export embedded in a host's own dev server.
  • The page carries serve-sim's preview marker.

A detected preview shows up as Simulator Preview with the device name when the state file is available. It never appears twice in the web server list. Because the preview page also needs serve-sim's stream and control WebSocket ports, moshi-hook reports those helper ports and Moshi forwards them together with the page port, so touch input works on the first connection.

Open a simulator

  • Home. Each paired host shows its running simulators next to its web servers. Tap one to open it.
  • Terminal. Tap the preview button in the terminal title, then pick the simulator from the sheet.
  • iPad sidebar. Simulators are listed under the host alongside web servers.
  • Moshi Desktop. The right rail has a Simulators section, and the ⌘K jump palette lists simulators under previews.

The preview opens in the in-app browser with the Simulator screen streamed live. Taps, drags, and multi-touch gestures on your phone go straight to the Simulator. Ports are forwarded to 127.0.0.1 on the device only, so nothing is reachable from your LAN or the internet.

Troubleshooting

  • No simulator listed, but the page loads on the Mac. Confirm the daemon sees it with moshi-hook servers. If the listener is missing, add its port with moshi-hook set scan-ports 3200 and wait for the next discovery refresh. See Hook settings.
  • Listed as a web server instead of a simulator. You are probably on a moshi-hook release older than 0.2.68, or serve-sim is running under a wrapper that hides its name. Update moshi-hook, and make sure the state directory under $TMPDIR/serve-sim/ is writable.
  • Video plays but touches do nothing. The control WebSocket port was not forwarded. Reopen the preview so Moshi forwards the helper ports again, and check that the state file under $TMPDIR/serve-sim/ exists, since that is where moshi-hook reads the helper ports from.
  • Stale entry after stopping serve-sim. Run npx @expo/serve-sim --kill to clear leftover helpers and state files. The next discovery sweep removes the entry.
  • Preview button hidden. Turn on Settings → Hooks → Use preview, then return to the terminal.