Protocol Used for Communication Between vphone-cli and vphoned: Length-Prefixed JSON over vsock
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 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, 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 to1in bothVPhoneControl.swiftandvphoned_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 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 and scripts/vphoned/vphoned_protocol.m. These Objective-C files provide the C helpers that mirror the client's framing logic:
// 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
// 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
// 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
// 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
// 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.swiftimplements high-level Swift APIs and theperformHandshakesequence. - Daemon Code:
vphoned_protocol.h/mprovidevp_read_message()andvp_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, 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 and implemented in scripts/vphoned/vphoned_protocol.m, providing the vp_read_message() and vp_write_message() functions that enforce the length-prefix convention.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →