# What Is the vphoned Guest Daemon in vphone-cli and How Does It Work?

> Discover the vphoned guest daemon in vphone-cli. Learn how this LaunchDaemon manages HID events, file operations, clipboard access, and auto-updates within iOS virtual machines via a JSON protocol over vsock.

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

---

**The vphoned guest daemon is a LaunchDaemon that runs inside iOS virtual machines created by vphone-cli, implementing a length-prefixed JSON protocol over vsock (port 1337) to handle HID events, file operations, clipboard access, and auto-updates from the host.**

The `vphoned` daemon serves as the bridge between the host macOS system and guest iOS virtual machines in the Lakr233/vphone-cli ecosystem. Written in Objective-C and running as a LaunchDaemon inside the virtualized iOS environment, it exposes a comprehensive control interface that enables automation, input simulation, and file management from the host side.

## Architecture Overview

The vphoned subsystem splits responsibilities between the host-side Swift client and the guest-side Objective-C daemon:

- **Host-side client** ([`sources/vphone-cli/VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/sources/vphone-cli/VPhoneControl.swift)): Opens a vsock connection, sends a *hello* handshake, queues asynchronous requests, parses responses, and implements retry logic on failure.
- **Guest daemon** (`scripts/vphoned/vphoned.m`): Listens on vsock port 1337, parses incoming length-prefixed JSON messages, dispatches them to feature modules, and returns JSON replies.
- **Feature modules** (`scripts/vphoned/*.h/.m`): Implement concrete operations for HID (`vphoned_hid.m`), files (`vphoned_files.m`), clipboard (`vphoned_clipboard.m`), keychain (`vphoned_keychain.m`), apps (`vphoned_apps.m`), and location (`vphoned_location.m`).
- **Protocol definition** ([`scripts/vphoned/vphoned_protocol.h`](https://github.com/Lakr233/vphone-cli/blob/main/scripts/vphoned/vphoned_protocol.h)): Declares JSON keys (`t`, `id`, `v`, etc.) and helper utilities for message framing.

## Connection Lifecycle and Protocol Flow

The **vphone-control protocol** operates over a virtio socket (vsock) connection to port 1337. The interaction follows a strict seven-step sequence:

1. **Host initiates vsock**: `VPhoneControl.connect(device:)` creates a `VZVirtioSocketConnection` to CID ANY, port 1337 (source: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), lines 45-57).

2. **Handshake**: The host sends a `hello` JSON containing the protocol version and optionally `bin_hash` (SHA-256 of the host binary) via the `writeMessage` method (source: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), lines 75-80).

3. **Guest replies**: The daemon reads the length-prefixed JSON using `readMessage` and responds with its capabilities, IP address, iOS version, and a `need_update` flag if the binary hashes differ (source: `vphoned.m`, lines 88-108).

4. **Auto-update**: If `need_update` is true and the variant permits updates, the host streams the binary via an `update` message with raw payload. The daemon writes it to `/var/root/Library/Caches/vphoned` via `receive_update` and exits (source: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift) lines 40-68 and `vphoned.m` lines 42-84).

5. **Read loop**: After handshake completion, the host starts a background read loop (`startReadLoop`) that continuously parses incoming JSON and dispatches it to pending request callbacks or fire-and-forget handlers (source: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), lines 78-85).

6. **Command dispatch**: Each JSON `t` value maps to a function in the appropriate feature module. For example, a `"hid"` command reaches `vphoned_hid.m` which invokes `vp_hid_key` or `vp_hid_press` (source: `vphoned.m`, lines 88-100).

7. **Response**: The daemon replies with JSON containing `t:"ok"` (or `t:"err"` on failure) and echoes the request `id`. The host resolves the pending `Continuation` and delivers the result to the caller.

## Core Feature Modules

Individual capabilities are implemented in separate translation units within `scripts/vphoned/`:

### HID Simulation (`vphoned_hid.m`)

Handles human interface device events by calling `vp_hid_key` (for press/release with boolean state) or `vp_hid_press` (for simple press). The module processes JSON commands with `page` and `usage` fields corresponding to USB HID usage tables.

### File Operations (`vphoned_files.m`)

Implements remote file system access including directory listing, file retrieval, upload, deletion, and renaming. The host can request operations like `"file_list"` with a path parameter, receiving an `"entries"` array in response.

### Clipboard Management (`vphoned_clipboard.m`)

Supports bidirectional clipboard synchronization for both text and image data. The daemon can get current clipboard contents or set new values from host commands.

### Keychain Access (`vphoned_keychain.m`)

Provides iOS keychain enumeration and entry addition capabilities, allowing automated certificate and credential management within the virtual machine.

### App Management (`vphoned_apps.m`)

Handles app listing, launch, termination, and foreground application queries. This enables automated UI testing and app lifecycle management from the host.

### Location Services (`vphoned_location.m`)

Receives GPS coordinate updates from the host and injects them into the iOS location subsystem, enabling location-based testing without physical movement.

## Auto-Update Mechanism

A distinctive feature of the vphoned guest daemon is its **self-updating capability**. When the handshake reveals a binary hash mismatch between host and guest, the host initiates a binary transfer:

```swift
// Host side: VPhoneControl.swift (lines 40-70)
await control.pushUpdate(fd: socketFD)

```

The guest receives the new binary through the `receive_update` function, writes it to `CACHE_PATH` (typically `/var/root/Library/Caches/vphoned`), sets executable permissions, and exits cleanly:

```objc
// Guest side: vphoned.m (lines 42-84)
if (receive_update(fd, size)) {
    exit(0);  // launchd restarts the daemon from CACHE_PATH
}

```

This design ensures the guest daemon stays synchronized with the host tooling without requiring manual VM reconfiguration or image rebuilds.

## Error Handling and Resilience

The host-side `VPhoneControl` class implements robust reconnection logic to handle VM reboots and network interruptions:

- **Per-attempt tokens**: Each connection attempt is tagged to distinguish stale callbacks from current operations.
- **Automatic reconnection**: On socket errors, the client schedules a reconnect after a configurable delay (`reconnectDelay`).
- **Pending request cleanup**: When disconnected, outstanding requests are cleared with a `ControlError.notConnected` error.
- **Request timeouts**: Different message types apply specific timeouts via `timeoutForRequest`, preventing indefinite hangs on slow operations.

## Practical Implementation Examples

### Sending HID Key Presses

From the host Swift client:

```swift
// Send left-control key press
await control.sendHIDPress(page: 0x07, usage: 0x00E0)

```

This serializes to JSON with `"t":"hid"` and writes to the vsock connection (implementation: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), lines 74-82).

### Guest-Side HID Handling

Inside the daemon (`vphoned.m`, lines 92-100):

```objc
if ([type isEqualToString:@"hid"]) {
    uint32_t page = [msg[@"page"] unsignedIntValue];
    uint32_t usage = [msg[@"usage"] unsignedIntValue];
    NSNumber *down = msg[@"down"];
    if (down) {
        vp_hid_key(page, usage, [down boolValue]);   // press/release
    } else {
        vp_hid_press(page, usage);                   // simple press
    }
}

```

### Retrieving File Listings

```swift
let entries = try await control.listFiles(path: "/var/mobile")
for entry in entries {
    print(entry["name"] ?? "unknown")
}

```

The host builds a `"file_list"` request; the daemon replies with an `"entries"` array (implementation: [`VPhoneControl.swift`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift), lines 82-87 and `vphoned_files.m`).

## Summary

- The vphoned guest daemon runs as a LaunchDaemon inside vphone-cli's iOS virtual machines, listening on vsock port 1337.
- It implements a length-prefixed JSON protocol for host-to-guest communication, defined in [`vphoned_protocol.h`](https://github.com/Lakr233/vphone-cli/blob/main/vphoned_protocol.h) and handled in `vphoned.m`.
- Feature modules separate concerns: HID input (`vphoned_hid.m`), file operations (`vphoned_files.m`), clipboard (`vphoned_clipboard.m`), and more.
- The auto-update mechanism allows the daemon to replace itself at `/var/root/Library/Caches/vphoned` and restart via launchd when the host binary changes.
- The host-side `VPhoneControl` Swift class manages connection lifecycle, reconnection logic, and provides async/await APIs for all daemon operations.

## Frequently Asked Questions

### How does vphoned handle protocol version mismatches?

During the initial handshake, both host and guest exchange protocol version identifiers in the `hello` message. If the versions are incompatible, the connection is terminated gracefully. For binary version mismatches detected via `bin_hash` comparison, the daemon sets a `need_update` flag, triggering the auto-update flow where the host streams a compatible binary before normal operations resume.

### What happens if the vsock connection drops during a file transfer?

The host-side `VPhoneControl` monitors the socket for read/write errors and enters a reconnection state with a configurable delay (`reconnectDelay`). Pending requests, including active file transfers, receive a `ControlError.notConnected` error through their async continuations. The client must retry the operation after the automatic reconnection completes, as the daemon does not maintain transfer state across connections.

### Can the auto-update feature be disabled for vphoned?

The auto-update behavior depends on the specific vphone-cli variant and build configuration. While the protocol supports skipping updates if the `need_update` flag is ignored by the host, the daemon is designed to eventually require matching binary versions for protocol stability. The update mechanism writes to `CACHE_PATH` and relies on launchd to restart the service, so disabling system-level LaunchDaemon management would prevent updates but also impair normal restart functionality.

### Which iOS versions does the vphoned guest daemon support?

The daemon targets modern iOS versions capable of running within the Virtualization.framework environment used by vphone-cli. Specific compatibility depends on the feature modules: HID simulation relies on private IOKit APIs available in iOS 14+, while file operations use standard Foundation APIs available across all supported virtualized iOS versions.