How vphone-cli Manages File Transfer Between Host macOS and Guest iOS VM

File transfer in vphone-cli operates over a virtio-vsock connection using a length-prefixed JSON protocol, where the host-side VPhoneControl class communicates with the guest-side vphoned daemon listening on port 1337 to execute upload, download, and list operations.

In the Lakr233/vphone-cli project, managing file transfer between the host macOS machine and the guest iOS VM is implemented through a direct vsock channel rather than traditional networking. This open-source virtualization tool leverages Apple's Virtualization framework to establish a socket connection between the host and guest, implementing a custom daemon-based architecture that handles file operations through structured JSON messaging.

Architecture Overview

The file transfer architecture consists of two primary components: a guest-side daemon and a host-side Swift client. The guest daemon, located at scripts/vphoned, runs inside the iOS virtual machine and listens on the well-known vsock port 1337 for incoming commands. The host client, implemented in sources/vphone-cli/VPhoneControl.swift, manages the connection lifecycle and exposes high-level Swift APIs that abstract the underlying socket communication.

The Guest-Side Daemon (vphoned)

Daemon Implementation

The vphoned daemon is a lightweight Objective-C service launched automatically when the guest VM boots. According to the source code in scripts/vphoned, this service creates a server socket using the virtio-vsock interface and binds to port 1337. It enters a listening loop that accepts incoming connections from the host, then processes length-prefixed JSON messages encoding file operation requests such as upload, download, or list.

Protocol Handling

When vphoned receives a message, it first reads the 4-byte length prefix to determine the JSON payload size. After parsing the command, the daemon executes the corresponding filesystem operation within the guest iOS environment. For uploads, it expects raw file data to follow the JSON header, which it writes to the specified guest path. The daemon responds with JSON status objects like { "status": "ok" } or error representations that propagate back to the host client.

The Host-Side Client (VPhoneControl)

Swift Implementation

The VPhoneControl class in sources/vphone-cli/VPhoneControl.swift serves as the primary host-to-guest file transfer interface. This class initializes a vsock connection using VZVirtioVsockDevice, configured during VM setup in sources/vphone-cli/VPhoneVirtualMachine.swift. It handles the 4-byte length prefixing required by the protocol, serializes Swift dictionaries to JSON, and parses responses into native data structures.

API Methods

VPhoneControl exposes three primary methods for file management:

  • uploadFile(localURL:remotePath:) - Streams a file from the host filesystem to the guest VM
  • downloadFile(remotePath:localURL:) - Retrieves a file from the guest and writes it to the host
  • listFiles(at:) - Returns an array of VPhoneRemoteFile objects populated from the daemon's directory listing response
let control = VPhoneControl(vsockPort: 1337)
try control.uploadFile(
    localURL: URL(fileURLWithPath: "/Users/me/report.pdf"),
    remotePath: "/Documents/report.pdf"
)

User Interface Integration

SwiftUI File Browser

The VPhoneFileBrowserView in sources/vphone-cli/VPhoneFileBrowserView.swift provides the graphical interface for file transfer. This SwiftUI view implements drag-and-drop handlers that invoke VPhoneControl.uploadFile when files are dropped onto the browser window. For downloads, the view presents context menus on file selections that trigger VPhoneControl.downloadFile, with progress indicators updating based on control layer callbacks.

Data Models

File metadata representation is handled by VPhoneRemoteFile in sources/vphone-cli/VPhoneRemoteFile.swift. This struct encapsulates properties including name, size, type, and icon, initialized from the JSON arrays returned by the daemon's list command. The file browser uses these model instances to render directory contents and handle user interactions.

The File Transfer Protocol

Message Format

All host-guest communication uses a length-prefixed JSON protocol. Each transmission begins with a 4-byte big-endian integer specifying the payload length, followed by the JSON data. This framing prevents stream corruption from partial reads and enables accurate message boundary detection. Raw file data for uploads appends immediately after the JSON header without additional prefixes.

// Example: Listing files programmatically
let files = try control.listFiles(at: "/Documents")
files.forEach { file in
    print("\(file.name) – \(file.size) bytes")
}

Transfer Flow

For uploads, the host sends a JSON command specifying the destination path, followed immediately by the raw file bytes. The daemon reads the length, parses the JSON to determine the target location, then consumes the remaining bytes as file content. For downloads, the host transmits a request JSON, and the daemon responds with a length-prefixed JSON header containing metadata, followed by the raw file data stream that the host writes to the local filesystem.

Error Handling and Robustness

The protocol implements graceful error propagation through JSON error objects rather than abrupt connection termination. When vphoned encounters filesystem errors or invalid paths, it returns structured error responses that the host client converts into Swift Error types. The length-prefixing mechanism ensures that temporary connection issues or partial reads do not desynchronize the protocol state, allowing the VPhoneFileBrowserView to display actionable error alerts to users.

Summary

  • vphone-cli implements file transfer using a vsock connection between host macOS and guest iOS VM via VZVirtioVsockDevice
  • The guest-side vphoned daemon (in scripts/vphoned) listens on port 1337 and processes length-prefixed JSON commands
  • The host-side VPhoneControl class (in VPhoneControl.swift) provides Swift APIs for uploadFile, downloadFile, and listFiles operations
  • The protocol uses 4-byte length prefixes to frame JSON messages and separate raw file data streams
  • The VPhoneFileBrowserView SwiftUI interface (in VPhoneFileBrowserView.swift) handles drag-and-drop interactions and progress reporting
  • Error handling converts daemon JSON responses into native Swift Error types for UI presentation and recovery

Frequently Asked Questions

What protocol does vphone-cli use for file transfer?

vphone-cli uses a custom protocol over virtio-vsock, a high-performance socket interface for host-guest communication in virtualized environments. The protocol transmits length-prefixed JSON messages over a stream socket on port 1337, where each message begins with a 4-byte integer indicating the payload length, followed by the JSON command and optional raw file data.

How does the guest VM receive files without network access?

The guest iOS VM communicates through the vsock device (VZVirtioVsockDevice) provided by Apple's Virtualization framework rather than traditional network interfaces. The vphoned daemon binds directly to this virtual socket interface, creating a direct memory-based communication channel that bypasses the TCP/IP stack entirely and operates independently of the guest's network configuration.

Can I use vphone-cli file transfer programmatically without the GUI?

Yes. The VPhoneControl class exposed in sources/vphone-cli/VPhoneControl.swift provides a programmatic API that operates independently of the SwiftUI interface. You can instantiate VPhoneControl with the vsock port parameter, then call methods like uploadFile(localURL:remotePath:) and downloadFile(remotePath:localURL:) directly from your Swift code without initializing VPhoneFileBrowserView.

What happens if a file transfer is interrupted?

The length-prefixing protocol ensures that partial reads are detected and handled gracefully. If a connection drops during transfer, the host client throws a Swift Error that propagates to the calling code. While the current implementation in scripts/vphoned does not support automatic resume for interrupted transfers, the atomicity of length-prefixed framing prevents file corruption by ensuring that incomplete writes are detectable by the protocol layer.

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 →