How vphone-cli Uses the vsock Protocol for Host-Guest Communication in Virtualized iOS

vphone-cli leverages Apple’s Virtualization Framework vsock (virtual socket) to establish bidirectional, IP-free communication between the macOS host and iOS guest, using port 1337 for JSON-based control messages and port 1338 for raw camera frame streaming.

vphone-cli is a command-line tool for running virtualized iOS devices on macOS using Apple's Virtualization Framework. At its core, the project relies on the vsock protocol to enable seamless host-guest communication without network bridging. This article examines how the Swift implementation configures virtual sockets, handles message framing, and manages multiple parallel data channels.

Configuring the vsock Device in the Virtual Machine

VM Setup and Socket Initialization

In VPhoneVirtualMachine.swift, the virtual machine configuration includes a VZVirtioSocketDeviceConfiguration instance to enable vsock communication. Lines 44-47 append this device to the VM's configuration, defining port 1337 as the primary control channel endpoint. This setup creates the foundation for the host-guest IPC mechanism.

// VM configuration excerpt from VPhoneVirtualMachine.swift
let socketDevice = VZVirtioSocketDeviceConfiguration()
// Device is added to VM configuration at lines 44-47

Control Channel Architecture (Port 1337)

Connection Establishment and Handshake

The VPhoneControl.swift file implements the host-side client for the control channel. The connect(device:) method stores the VZVirtioSocketDevice reference and initiates connection attempts via attemptConnect() (lines 45-57). Once connected, performHandshake(fd:attemptToken:) (lines 75-85) executes a protocol handshake where the host transmits a JSON "hello" message prefixed by a 32-bit big-endian length header.

// Establish control channel
let control = VPhoneControl(variant: .regular)
control.connect(device: vm.virtualMachine.socketDevices.first as! VZVirtioSocketDevice)

Message Framing Protocol

Every packet exchanged over the vsock channel follows a strict binary format:


[uint32 big-endian length][UTF-8 JSON payload]

The JSON structure consistently includes three fields: "v" for protocol version, "t" for message type, and an optional "id" for request correlation. Helper functions writeMessage(fd:dict:) and readMessage(fd:) handle the length-prefixing logic, ensuring byte-stream boundary integrity.

Asynchronous Request/Response Pattern

To support concurrent operations, the control channel implements an async request/response system. Each request carries a unique "id" generated from nextRequestId. The host stores pending callbacks in a thread-safe pendingRequests dictionary via addPending (lines 72-78). When responses arrive, removePending (lines 84-87) retrieves and invokes the appropriate callback before removing the entry. The sendRequest method (lines 52-78) orchestrates this flow.

High-Level Control APIs

VPhoneControl exposes convenience methods that construct JSON payloads and transmit them over the vsock control channel:

  • HID Events: sendHIDPress, sendHIDDown, and sendHIDUp transmit "hid" messages (lines 74-84) for simulating hardware button presses.
  • Touch Injection: sendTouch(phase:x:y:) sends "touch" messages (lines 106-118) to inject touch events at normalized screen coordinates.
  • Developer Mode: sendDevModeStatus() queries the guest's developer mode state (lines 124-133).
  • File Operations: listFiles, downloadFile, and uploadFile utilize the generic sendRequest helper (lines 80-99) for filesystem management.
// Send HID key press (e.g., Home button)
control.sendHIDPress(page: 0x0C, usage: 0x01)

// Inject touch at screen center
control.sendTouch(phase: 0, x: 0.5, y: 0.5)

// Query files asynchronously
Task {
    let entries = try await control.listFiles(path: "/var/mobile/Containers/Data/Application")
}

Binary Auto-Update Mechanism

When guestBinaryURL is configured, the host calculates the binary's SHA-256 hash and includes it in the initial handshake as guestBinaryHash. If the guest responds with "need_update": true, the pushUpdate(fd:) method (lines 40-70 in VPhoneControl.swift) streams the raw binary data after sending a header with "t":"update" and the payload size.

Camera Streaming via Secondary vsock (Port 1338)

For high-throughput video data, VPhoneCameraServer.swift establishes a secondary vsock connection on port 1338. Lines 7-16 configure this independent channel to avoid blocking the control socket. The host connects via device.connect(toPort: Self.vsockPort) and, once established, repeatedly generates BGRA frames. The send(fd:frame:) method (invoked in lines 24-44) transmits length-prefixed frame data using a format optimized for raw pixel buffers rather than JSON.

// Camera server setup
let camera = VPhoneCameraServer()
camera.setSource(.testPattern)
camera.connect(device: vm.virtualMachine.socketDevices.first as! VZVirtioSocketDevice)
camera.startStreaming()

Resilience and Reconnection Logic

Both the control and camera components implement exponential back-off reconnection strategies through scheduleReconnect and attemptConnect methods. This ensures automatic recovery when the guest VM restarts or temporary vsocket interruptions occur, maintaining session continuity without manual intervention.

Summary

  • vphone-cli uses Apple's Virtualization Framework vsock to create isolated, high-performance communication channels between macOS host and iOS guest.
  • The control channel (port 1337) in VPhoneControl.swift handles JSON-based RPC, binary updates, and device input simulation using length-prefixed message framing.
  • A dedicated camera channel (port 1338) in VPhoneCameraServer.swift streams raw BGRA frames independently from control traffic.
  • Asynchronous request/response correlation uses unique IDs stored in thread-safe dictionaries to handle concurrent operations.
  • Exponential back-off reconnection logic ensures robust communication across guest VM restarts.

Frequently Asked Questions

What port numbers does vphone-cli use for vsock communication?

vphone-cli allocates port 1337 for the control channel (JSON RPC and device commands) and port 1338 for the camera streaming channel. These are hardcoded in VPhoneVirtualMachine.swift and VPhoneCameraServer.swift respectively.

How does vphone-cli handle message boundaries in the vsock byte stream?

The protocol uses a 32-bit big-endian length prefix followed by UTF-8 JSON data. This framing ensures that variable-length messages are correctly reconstructed from the sequential byte stream without TCP/IP-style packet boundaries.

Can the host push updated binaries to the iOS guest automatically?

Yes. If guestBinaryURL is specified, the host includes the binary's SHA-256 hash in the initial handshake. The guest checks this against its current version and requests updates by responding with "need_update": true, triggering the pushUpdate(fd:) method to stream the new binary.

What happens if the vsock connection drops during operation?

Both VPhoneControl and VPhoneCameraServer implement scheduleReconnect with exponential back-off. They automatically attempt to re-establish the vsock connection when the guest becomes available again, ensuring resilience against temporary disconnections or guest VM restarts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →