The problem

Cloud dev environments assume you're at a laptop. Daytona gives you a Linux sandbox in seconds — but the moment it needs a GUI, you're stuck: noVNC in mobile Safari is a 1024×768 canvas overflowing a phone screen, with no way to raise the keyboard.

We spend a lot of time driving long-running agents from a phone. The gap was always the same: the agent opens a browser, or a dialog appears, or something needs a click — and you have to go find a laptop.

What we built

Daytona in Your Pocket is an open-source plugin for Mobile SSH, an SSH client for iOS and Android. It turns a phone into a full control surface for Daytona sandboxes:

  • Lifecycle — create, start, stop and delete sandboxes, with autoStopInterval and autoDeleteInterval set at creation so a forgotten sandbox stops billing on its own.
  • Tap-to-click desktop — a live screenshot stream from Daytona Computer Use. Tap to click, long-press to right-click, drag to drag, plus an Esc/Tab/^C key bar and a text field that types into the desktop.
  • Full noVNC — one tap opens the real desktop over a signed preview URL.

How it's built

A Mobile SSH plugin is interpreted JavaScript in a WebView — never compiled code — so it can be fetched from GitHub at runtime and still satisfy both app stores. The plugin talks to the host over a window.MobileSSH bridge:

  • MobileSSH.http.fetch — native HTTP, so no CORS and the API key travels in an Authorization header, never in a URL and never in the log.
  • MobileSSH.storage.putSecret — the Daytona key is stored in Keychain / Keystore.
  • MobileSSH.ui.openService — opens noVNC in a separate, bridge-less WebView.

Two Daytona surfaces do the work: the control plane at app.daytona.io/api for sandbox CRUD and signed preview URLs, and the sandbox's toolbox proxy for /computeruse/*screenshot/compressed, mouse/click, mouse/drag, mouse/scroll, keyboard/key, keyboard/hotkey, keyboard/type.

Challenges

1. Sending input can brick Computer Use. If input arrives while the computer-use processes aren't running, the sandbox daemon's plugin child dies — after which even POST /computeruse/start answers 503 connection is shut down, and the only recovery is a full sandbox stop+start. Worse, a sandbox can auto-stop underneath an open plugin, because work inside the sandbox doesn't count as activity. So every input call is gated on positive evidence that computer-use is live: a successful frame or a /status probe, trusted for 30 seconds. Screenshots and /status are safe in any state; only input bricks it. When it happens anyway, the UI surfaces a "Restart desktop" button that runs the stop+start.

2. A WebView can't set headers on subresources. noVNC loads its own JS, CSS and a wss:// websocket — none of which carry an Authorization header. Daytona's signed preview URL solves it exactly: the token lives in the hostname, so the HTML, every subresource and the websocket all authenticate with no headers at all. (vnc.html, not vnc_lite.html — vnc_lite ignores autoconnect and resize, and has no control bar, so there's no way to raise the soft keyboard.)

3. That signed URL is a bearer credential. The desktop behind it has no VNC password — x11vnc offers RFB security type "None" — so anyone with the link has full mouse and keyboard control. We revoke the token on exit instead of leaving it alive for its TTL, and the sandbox's own web origin is never loaded in the bridged WebView, where it would inherit the plugin's capabilities.

4. Phones pay for bytes. We measured a stock desktop at 1024×768: PNG at 0.6 scale is 84 KB per frame, JPEG q55 at 0.6 is 20 KB. And an idle desktop returns a byte-identical frame every poll — so identical frames trigger an exponential backoff from 900 ms out to 6 s, dropping an idle session from ~20 KB/s to ~3 KB/s. Any tap snaps it back to full rate. Three quality presets let you choose ~13 KB, ~20 KB or ~150 KB per frame.

5. Small sharp edges. GET /sandbox is eventually consistent, so a list right after a create comes back without the new sandbox — we use the create response instead. No snapshot is specified on create, because the org default image is the only one shipping the Xvfb/xfce4/x11vnc/noVNC stack Computer Use needs. And window.confirm() is unreliable in these WebViews, so destructive buttons use a two-tap arm-then-confirm.

What we learned

Mobile isn't a smaller desktop — it's a different input model. Once we stopped porting a mouse and started designing for taps, long-presses and drags, a 6-inch screen turned out to be enough to drive a cloud Linux box.

What's next

Streaming frames over a websocket instead of polling, an agent mode where a model drives the same Computer Use endpoints while you watch on the phone, and multi-sandbox switching.

Built With

Share this project:

Updates

Submission history