Back to list

Blog

Beyond SSH: Why WebSocket Beats Terminal Protocols for AI Coding Agents

SSH was designed for interactive human sessions. AI coding agents need something different. Here's why WebSocket is the better protocol for remote agent control.

Published Tags: engineering / architecture / comparison / seo
Blog

Follow product and engineering updates from this channel.

Browse category

When developers first hear about controlling Claude Code from a phone, the natural reaction is: "Why not just SSH into my Mac?"

It's a reasonable question. SSH is proven, secure, ubiquitous. Every developer knows it. There are excellent mobile SSH clients like Termius, Blink Shell, and Prompt. Why build something different?

The answer isn't that SSH is bad. It's that AI coding agents have fundamentally different interaction patterns than interactive shell sessions, and those differences compound into a worse experience when you try to force-fit SSH.

The Interaction Model Mismatch

SSH was designed in 1995 for a specific model: a human sits at a terminal, types commands, reads output, types more commands. The session is interactive and synchronous — you're actively engaged the entire time.

AI coding agents invert this model:

SSH (Interactive Shell)AI Agent Control
Session durationMinutes to hours of active useHours to days, mostly idle monitoring
Human attentionContinuousIntermittent — check when notified
Input patternFrequent keystrokesOccasional prompts + approval decisions
Output patternResponse to your commandsLong autonomous streams you scan, not read
Critical eventsYou see them because you're watchingYou might miss them if you're not watching
Failure modeYou notice immediatelyAgent might wait indefinitely for approval

The last two rows are the problem. When Claude Code finishes a task or needs your approval for a dangerous operation, SSH has no way to tell you. You have to be watching.

Problem 1: No Push Notifications

This is the biggest gap. With an AI coding agent, the most valuable workflow is:

  1. Send a task: "Refactor the payment module to use the new API"
  2. Put your phone away
  3. Get notified when Claude finishes or needs input
  4. Review and continue

SSH cannot do step 3. The SSH protocol has no mechanism for pushing notifications to a mobile device. The TCP connection exists only while your SSH client is in the foreground (or using Mosh, while the UDP session is alive). Even then, there's no bridge between "data appeared in the terminal" and "send a push notification to this device."

You can hack around this with shell scripts that send notifications via external services, but that's fragile infrastructure you have to build and maintain yourself.

WebSocket approach: The server monitors Claude Code's output and hook events. When it detects a completion or approval request, it pushes a structured event to connected clients. The iOS app converts these into native push notifications. No polling, no external services.

Problem 2: iOS Background Behavior

iOS aggressively suspends apps that aren't in the foreground. An SSH client that goes to the background gets roughly 30 seconds before iOS suspends its network connections. After that:

  • SSH: Connection drops. When you reopen the app, you get a "broken pipe" error. You reconnect, but you've lost your scroll position and any output that happened while you were away.
  • Mosh: Better — it uses UDP and can resume. But you still miss everything that happened during suspension. And Mosh doesn't solve the notification problem.

This is a fundamental iOS constraint, not a bug in SSH clients. Apple doesn't allow arbitrary background network connections for battery reasons.

WebSocket approach: The server buffers terminal output regardless of client connection state. When the iOS app returns to the foreground, it reconnects automatically and performs a multi-stage state hydration — refreshing at 0.2s, 0.7s, and 1.5s after reconnecting to capture the full current state. Meanwhile, critical events (task completion, errors) are delivered as push notifications even when the app is suspended.

Problem 3: Session State Management

AI coding agents often run multiple parallel tasks. You might have:

  • Session A: refactoring the auth module
  • Session B: writing tests for the API layer
  • Session C: fixing a production bug

With SSH, managing this means:

  1. SSH into your Mac
  2. Manually attach to the right tmux session: tmux attach -t session-a
  3. Check status, detach: Ctrl+B, D
  4. Attach to the next session: tmux attach -t session-b
  5. Repeat

It works, but it's manual and error-prone on a phone keyboard.

WebSocket approach: The server exposes session management as structured commands. The iOS app shows a list of active sessions with their current states. Tap to switch. Create new sessions with a path picker that browses your Mac's filesystem. Kill sessions with a swipe. The server handles all the tmux orchestration.

Problem 4: Agent Event Semantics

SSH transports raw bytes. It doesn't understand what's happening in the terminal. It doesn't know the difference between Claude thinking, Claude writing code, Claude waiting for approval, or Claude finishing a task.

Claude Code has a hook system that fires shell scripts on specific events: task start, task completion, notification events. These hooks are how the server learns about agent state transitions.

SSH limitation: There's no clean way to route these hook events through an SSH session. You'd need a separate channel (another SSH connection, a webhook service, something) running alongside your terminal session.

WebSocket approach: The server receives hook events via HTTP POST from Claude Code's hook scripts. It parses them, deduplicates them (with a 3-second window to avoid duplicate alerts), and pushes them to the iOS app over the same WebSocket connection that carries terminal data. One connection, multiple data types.

Problem 5: Authentication and Access Control

SSH authentication is powerful but complex for mobile use cases:

  • Key management on iOS is awkward
  • You need to configure sshd on your Mac
  • Port forwarding through NAT requires additional setup
  • Exposing SSH to the internet demands careful security configuration

WebSocket approach: Tactic Remote uses API key authentication over WebSocket. For LAN connections, the API key is optional (your home network is the trust boundary). For Cloudflare Tunnel connections, the API key is required. The Mac app generates a 32-character random key and transfers it via QR code — no key file management, no sshd configuration.

This is a deliberately simpler security model. It trades SSH's flexibility for ease of setup. If you need the security guarantees of SSH public key authentication, SSH is the right tool — but probably for a different use case.

When SSH Is Still Better

SSH wins when:

  • You manage many servers — SSH is the standard protocol for server access. One client, many hosts.
  • You need file transfer — SFTP/SCP are built into SSH. Tactic Remote doesn't do file transfer.
  • You use non-Claude tools — SSH is agent-agnostic. It works with any terminal application.
  • You need port forwarding — SSH tunnels are battle-tested for proxying services.
  • Your compliance requirements mandate SSH — some organizations require SSH for audit and access control.

The Architecture in Practice

Here's what the WebSocket architecture looks like for Tactic Remote:

iPhone App                    Mac Server                    Claude Code
    │                             │                              │
    │  WebSocket (ws:// or wss://)│                              │
    │◄───────────────────────────►│  tmux send-keys              │
    │  • Terminal I/O             │─────────────────────────────►│
    │  • Session commands         │  tmux capture-pane           │
    │  • Event stream             │◄─────────────────────────────│
    │                             │                              │
    │  Push notification          │  HTTP POST /hook             │
    │◄────────────────────────────│◄─────────────────────────────│
    │  (via WebSocket event)      │  (Claude Code hook scripts)  │
    │                             │                              │

The server sits between the phone and Claude Code, adding:

  • Event semantics (raw terminal bytes become structured events)
  • Session management (one server, many tmux sessions)
  • Reconnection resilience (buffered state, automatic hydration)
  • Notification routing (hook events become push notifications)

SSH would give you the raw terminal connection (the top arrows), but none of the intelligence in between.

What About Mosh?

Mosh (Mobile Shell) is the most common suggestion for improving SSH on mobile. It uses UDP instead of TCP, which helps with:

  • Network roaming (switching between WiFi and cellular)
  • Connection persistence (the server keeps state even when the client disconnects)
  • Local echo (instant keystroke feedback regardless of latency)

Mosh genuinely improves the mobile SSH experience. But it still doesn't solve the fundamental issues:

  • No push notifications
  • No agent event awareness
  • No structured session management
  • Still subject to iOS background suspension (though recovery is faster)

If you're committed to SSH-based tools, Blink Shell with Mosh is the best iOS option. It's a good tool solving a different problem.

Conclusion

The question isn't "SSH or WebSocket?" as an abstract protocol comparison. It's "what interaction model does your workflow need?"

If you're managing servers and running interactive commands, SSH is the right protocol with decades of proven reliability.

If you're controlling an AI agent that runs autonomously, needs your attention intermittently, and benefits from push notifications and structured event handling, a purpose-built WebSocket architecture solves problems that SSH cannot — not because SSH is flawed, but because it was designed for a different era of human-computer interaction.

Try Tactic Remote

Control your coding Agents from your phone

Connect to Claude Code, Codex, and other Agents on your Mac, Windows, or Linux computer. Check progress and send the next instruction from iPhone or iPad.