# Protocol Used for Communication Between vphone-cli and vphoned: Length-Prefixed JSON over vsock

> Discover how vphone-cli and vphoned communicate. Learn about the length-prefixed JSON protocol over vsock for seamless client-daemon interaction. Essential for vphone-cli users.

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

---

**The vphone-cli client communicates with the vphoned daemon using a length-prefixed JSON protocol transmitted over a vsocket (vsock) connection on port 1337, where each message consists of a 32-bit big-endian length prefix followed by a UTF-8 JSON payload containing version, type, and request-specific fields.**

The communication protocol between the host-side **vphone-cli** client and guest-side **vphoned** daemon in the [Lakr233/vphone-cli](https://github.com/Lakr233/vphone-cli) repository is a lightweight, self-describing messaging system built on Apple's Virtualization framework. This protocol enables secure, bi-directional command and data exchange between macOS host and iOS guest environments without external network dependencies.

## Transport Layer and Message Framing

### vsock Transport on Port 1337

The protocol operates over a **vsock** (virtual socket) channel provided by Apple's virtualization framework. In [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), the client establishes a `VZVirtioSocketDevice` connection targeting port **1337** to reach the daemon running inside the virtualized iOS environment.

### Length-Prefixed Binary Framing

Every message follows a strict binary framing convention to eliminate parsing ambiguity:

- **32-bit big-endian integer**: Specifies the exact byte length of the JSON payload that follows.
- **UTF-8 JSON payload**: The actual message content encoded as a JSON dictionary.

This design allows the receiver to know precisely how many bytes to read for the next complete message before parsing begins.

## JSON Message Structure and Schema

Each JSON payload transmitted between **vphone-cli** and **vphoned** must include three core keys:

- **`"v"`**: Integer protocol version (currently hardcoded to `1` in both [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift) and [`vphoned_protocol.h`](https://github.com/Lakr233/vphone-cli/blob/main/vphoned_protocol.h)).
- **`"t"`**: String message type identifier (e.g., `"hello"`, `"ping"`, `"hid"`, `"touch"`, `"file_list"`).
- **`"id"`**: Optional string request identifier that the daemon echoes back in responses, enabling asynchronous request/response correlation.

Additional fields are appended per message type. For example, HID events include `"page"` and `"usage"` keys, touch events require `"phase"` (0=down, 1=move, 3=up) along with `"x"` and `"y"` coordinates normalized to 0-1, and file operations use `"path"` to specify target directories.

## Implementation Details

### Client-Side Protocol Stack

The Swift implementation in [`sources/vphone-cli/VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneControl.swift) manages the connection lifecycle, handshake, and high-level API. The `performHandshake` method (lines 73-104) initiates the session by transmitting `{"v":1,"t":"hello"}` and processing the daemon's capabilities response, which includes the daemon name, IP address, iOS version, and a `need_update` flag.

Key methods handling protocol specifics include:

- **`sendRequest()`**: Constructs the base dictionary, injects protocol version `"v"` and unique `"id"`, then calls low-level I/O.
- **`sendPing()`**: Health check using the `"ping"` message type.
- **`sendTouch()`**: Injects touch events with phase and coordinate data.
- **`listFiles()`**: Requests directory listings via the `"file_list"` type.

### Daemon-Side Framing Utilities

The daemon implements the framing layer in [`scripts/vphoned/vphoned_protocol.h`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/vphoned/vphoned_protocol.h) and `scripts/vphoned/vphoned_protocol.m`. These Objective-C files provide the C helpers that mirror the client's framing logic:

```c
// vphoned_protocol.h
BOOL vp_read_fully(int fd, void *buf, size_t count);
BOOL vp_write_fully(int fd, const void *buf, size_t count);
NSDictionary *vp_read_message(int fd);          // reads one length-prefixed JSON dict
BOOL vp_write_message(int fd, NSDictionary *dict); // writes a length-prefixed JSON dict

```

The `vp_read_message()` function first reads the 4-byte length prefix, allocates a buffer of that size, reads the remaining bytes, and deserializes the JSON using `NSJSONSerialization`. The `vp_write_message()` function performs the inverse operation.

## Practical Code Examples

### Sending a Ping Request from Swift

```swift
// VPhoneControl.swift – send a ping and await the reply
func sendPing() async throws {
    // The request dict is automatically enriched with "v" and a unique "id"
    _ = try await sendRequest(["t": "ping"])
}

```

### Reading Messages on the Daemon Side

```objc
// vphoned_protocol.m – read one message from the vsock fd
NSDictionary *msg = vp_read_message(fd);
if (msg) {
    NSLog(@"Received: %@", msg);
    // Process according to msg[@"t"]
}

```

### Injecting Touch Events from the Client

```swift
// VPhoneControl.swift – inject a single-finger touch
func sendTouch(phase: Int, x: Double, y: Double) {
    nextRequestId += 1
    let msg: [String: Any] = [
        "v": Self.protocolVersion,
        "t": "touch",
        "id": String(nextRequestId, radix: 16),
        "phase": phase,      // 0=down, 1=move, 3=up
        "x": x,              // normalized 0-1 coordinates
        "y": y
    ]
    writeMessage(fd: connection!.fileDescriptor, dict: msg)
}

```

### Listing Files in the Guest Sandbox

```swift
// VPhoneControl.swift – request a directory listing
func listFiles(path: String) async throws -> [[String: Any]] {
    let (resp, _) = try await sendRequest(["t": "file_list", "path": path])
    return resp["entries"] as? [[String: Any]] ?? []
}

```

## Summary

- **Transport**: Virtual socket (vsock) on port 1337 using `VZVirtioSocketDevice`.
- **Framing**: 32-bit big-endian length prefix followed by UTF-8 JSON payload.
- **Core Schema**: Every message requires `"v"` (version), `"t"` (type), and optionally `"id"` (request correlation).
- **Client Code**: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift) implements high-level Swift APIs and the `performHandshake` sequence.
- **Daemon Code**: `vphoned_protocol.h/m` provide `vp_read_message()` and `vp_write_message()` for Objective-C framing.
- **Capabilities**: Supports HID injection, touch events, file system traversal, device information queries, and health pings.

## Frequently Asked Questions

### What transport protocol does vphone-cli use to communicate with vphoned?

The client uses **vsock** (virtual socket) provided by Apple's Virtualization framework, connecting to port 1337 on the guest's `VZVirtioSocketDevice`. This provides host-to-guest communication without exposing network interfaces externally.

### How are messages framed in the vphone-cli protocol?

Messages use **length-prefixed binary framing**: a 32-bit big-endian integer declares the payload size, immediately followed by that many bytes of UTF-8 encoded JSON. This allows the receiver to allocate exactly the required buffer size before parsing.

### How does vphone-cli match asynchronous responses to requests?

The protocol supports optional request correlation via the **`"id"`** field. When the client includes a unique string identifier in a request dictionary, the daemon echoes this same identifier in its response, enabling the client to route asynchronous replies to the correct awaiting call sites.

### Where are the protocol implementations located in the source code?

The client-side implementation resides in **[`sources/vphone-cli/VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneControl.swift)**, which handles connection setup, the `performHandshake` method, and high-level message construction. The daemon-side framing utilities are defined in **[`scripts/vphoned/vphoned_protocol.h`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/vphoned/vphoned_protocol.h)** and implemented in **`scripts/vphoned/vphoned_protocol.m`**, providing the `vp_read_message()` and `vp_write_message()` functions that enforce the length-prefix convention.