How the vphone-cli `vm clone` Command Performs APFS Snapshot-Based Cloning with Fresh Device Identity

The vphone-cli clone command creates a lightweight copy-on-write (CoW) clone of a VM bundle using the macOS clonefile(2) system call, then resets the device identity by clearing NVRAM, UDID predictions, and the machine identifier in the manifest.

The vphone-cli tool from the Lakr233/vphone-cli repository provides efficient virtual machine management for iOS device emulation on macOS. Its vm clone functionality leverages APFS filesystem features to create near-instantaneous VM copies, then sanitizes hardware-specific identifiers to ensure the cloned instance appears as a fresh device to iOS and Apple's activation servers.

APFS Copy-on-Write Cloning Mechanism

The core cloning operation relies on the macOS APFS filesystem's ability to create lightweight snapshots that share underlying storage blocks until modified.

The clonefile(2) System Call

In sources/VPhoneCore/VPhoneBundleOps.swift, the clone() method invokes the low-level clonefile(2) system call to perform the actual copy-on-write operation. This creates a new directory entry that points to the same physical blocks as the source bundle, completing in milliseconds regardless of file size.

// VPhoneBundleOps.clone – lines 147-150
if clonefile(src.path, dst.path, 0) != 0 {
    try? fm.removeItem(at: dst)               // clear any partial clonefile output
    try fm.copyItem(at: src, to: dst)          // fallback copy
}

When clonefile() succeeds, the destination becomes an APFS snapshot of the source, consuming negligible additional disk space until either the original or clone is modified.

Fallback to Standard File Copy

If the target volume does not support APFS—indicated by clonefile() returning a non-zero value—the implementation automatically falls back to FileManager.copyItem. The code first attempts to clean up any partial clonefile output before performing a conventional recursive copy, ensuring robustness across different storage configurations.

Resetting Device Identity for Fresh Activation

After the filesystem clone completes, the command transforms the copied bundle into a distinct virtual device by stripping all hardware-specific identifiers.

Removing Identity Artifacts

The resetIdentity() method in VPhoneBundleOps.swift (lines 55-66) deletes files that tie the VM to its previous hardware state:

  • nvram.bin: Contains non-volatile RAM settings including device-specific boot parameters
  • udid-prediction.txt: Stores predicted unique device identifiers
  • *.shsh: SHSH blob caches used for firmware signing verification
// VPhoneBundleOps.resetIdentity – lines 55-66
for name in ["nvram.bin", "udid-prediction.txt"] { … }
for u in entries where u.pathExtension == "shsh" { … }
let manifest = try VPhoneVirtualMachineManifest.load(from: configURL)
try manifest.updating(machineIdentifier: Data()).write(to: configURL)

Clearing the Machine Identifier

The code loads the bundle's config.plist via VPhoneVirtualMachineManifest, replaces the machineIdentifier field with an empty Data() value, and persists the updated manifest. This ensures the cloned VM generates a new machine identifier on next boot, appearing as a fresh device to iOS.

CLI Command Structure

The command-line interface in sources/vphone-cli/VPhoneVMTransferCLI.swift provides the user-facing entry point for the cloning operation.

Command Resolution and Execution

Lines 5-21 of VPhoneVMTransferCLI.swift resolve the source and destination VM names using VPhoneVMSelection, then delegate to the core cloning logic:

// VPhoneVMTransferCLI.swift – lines 5-21
let name    = try VPhoneVMSelection.resolveExisting(name,   in: lib.library)
let newName = try VPhoneVMSelection.resolveNewName(newName, prompt: "New VM name:")
let clone   = try VPhoneBundleOps.clone(bundleNamed: name, to: newName, in: lib.library)
print("cloned \(name) → \(clone.name)")

This separation of concerns allows the CLI to handle user interaction while delegating filesystem operations to VPhoneBundleOps.

Usage Examples

Shell Execution

Clone an existing VM named "myPhone" to a new instance called "myPhone-clone":

vphone-cli clone myPhone myPhone-clone

Programmatic Integration

For automation or custom tooling, import the VPhoneCore framework:

import VPhoneCore

let library = try VPhoneLibrary(at: URL(fileURLWithPath: "/path/to/vphone/library"))
let clonedBundle = try VPhoneBundleOps.clone(
    bundleNamed: "myPhone",
    to: "myPhone-clone",
    in: library
)
// clonedBundle points to a VM with reset identity

Summary

  • APFS CoW Cloning: The clone command uses clonefile(2) to create space-efficient snapshots that share underlying blocks with the source VM.
  • Automatic Fallback: When APFS is unavailable, the system transparently falls back to FileManager.copyItem for standard file copying.
  • Identity Sanitization: The process removes nvram.bin, udid-prediction.txt, and SHSH files to eliminate hardware fingerprints.
  • Manifest Reset: The machineIdentifier field in config.plist is cleared to force generation of a fresh device identity on next boot.
  • Performance: Copy-on-write operations complete in milliseconds regardless of VM bundle size, making cloning effectively instantaneous.

Frequently Asked Questions

What happens if my destination drive is not formatted as APFS?

If the destination volume does not support APFS, the clonefile(2) call fails and the implementation automatically falls back to FileManager.copyItem, performing a conventional recursive copy. This ensures compatibility with HFS+ or external drives while sacrificing the space and speed benefits of copy-on-write cloning.

Why does the clone command need to reset the device identity?

iOS and Apple's activation servers track devices using unique identifiers stored in NVRAM and the machine manifest. Without clearing nvram.bin, udid-prediction.txt, and the machineIdentifier field, the cloned VM would retain its parent's hardware identity, causing conflicts with activation, iCloud, and enterprise MDM systems. The reset ensures the clone appears as a physically distinct device.

How much disk space does a cloned VM consume initially?

Because clonefile(2) creates a copy-on-write snapshot, the cloned bundle initially consumes only the metadata overhead required for the new directory structure—typically a few kilobytes. Storage usage increases only as files are modified in either the source or clone, at which point APFS allocates new blocks for the changed data.

Can I clone a VM to the same name as the source?

No, the CLI requires distinct source and destination names. The VPhoneVMSelection.resolveNewName() function validates that the target name does not already exist in the library, preventing accidental overwrites and ensuring the operation creates a distinct VM entry.

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 →