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

> Discover how vphone-cli handles host macOS to guest iOS VM file transfer. Learn about virtio-vsock, JSON protocol, and communication between VPhoneControl and vphoned for seamless uploads and downloads.

- Repository: [Lakr/vphone-cli](https://github.com/Lakr233/vphone-cli)
- Tags: internals
- Published: 2026-09-10

---

**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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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

```swift
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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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.

```swift
// 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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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.