How vphone-cli Handles Virtual Machine Networking: Architecture and Implementation

vphone-cli configures virtual machine networking using Apple's Virtualization.framework through a declarative manifest system that validates settings in VPhoneNetworking.swift and supports NAT, bridged, and disabled attachment modes.

vphone-cli is a Swift-based command-line tool that orchestrates Linux virtual machines on macOS using Apple's Virtualization.framework. The project implements a type-safe abstraction layer for VM networking that centralizes validation logic in the VPhoneCore module while delegating device instantiation to the framework's native APIs. Understanding how vphone-cli handles networking requires examining three tightly-coupled components that manage configuration parsing, validation, and runtime injection.

Core Architecture Components

VPhoneNetworking.swift

Located at sources/VPhoneCore/VPhoneNetworking.swift, this file contains the central networking orchestrator. It exposes static methods that bridge the gap between user-facing configuration strings and concrete VZVirtioNetworkDeviceConfiguration objects.

Key responsibilities include:

  • availableBridgeInterfaces() – Discovers host network interfaces suitable for bridging via VZBridgedNetworkInterface.networkInterfaces (lines 42-67)
  • makeNetworkDevice() – Constructs the appropriate network attachment based on the NetworkMode enum, returning VZNATNetworkDeviceAttachment, VZBridgedNetworkDeviceAttachment, or nil (lines 92-116)
  • merge() – Combines existing manifest configuration with CLI-provided overrides to produce a validated NetworkConfig

VPhoneVirtualMachine.swift

The runtime component lives in sources/vphone-cli/VPhoneVirtualMachine.swift. During initialization (around lines 87-92), this class loads the persisted networkConfig from the VM manifest, invokes VPhoneNetworking.makeNetworkDevice(), and injects the resulting device into the VZVirtualMachineConfiguration.networkDevices array.

If networking is disabled, makeNetworkDevice() returns nil, resulting in an empty networkDevices array and a VM with no virtual NIC.

VPhoneVMCLI.swift

The user interface layer in sources/vphone-cli/VPhoneVMCLI.swift exposes the --network and --bridge-interface flags. It translates raw string arguments (like "bridged" or "nat") into the NetworkMode enum (.nat, .bridged, .off) and calls VPhoneBundleOps.updateConfig to persist changes to the manifest (lines 38-66).

Network Modes Explained

NAT Mode (Default)

When NetworkConfig.mode is set to .nat, makeNetworkDevice() returns a VZVirtioNetworkDeviceConfiguration initialized with VZNATNetworkDeviceAttachment. This mode requires no additional configuration; macOS automatically handles IP address assignment and routes VM traffic through the host's internet connection using Network Address Translation.

Bridged Mode

Bridged networking requires a valid host interface name (e.g., en0). The implementation calls resolveBridgeInterface() to select the requested, saved, or first available bridge, then constructs a VZBridgedNetworkDeviceAttachment(interface:) instance. In this mode, the VM appears as a distinct host on the local Ethernet segment, receiving its own IP address from the network's DHCP server rather than sharing the host's NAT layer.

Disabled Mode

Setting the mode to .off causes makeNetworkDevice() to return nil. Consequently, config.networkDevices remains empty, and the virtual machine boots with no network connectivity.

Host-Only Limitations

The framework explicitly blocks host-only networking. If selected, the code throws VPhoneNetworkingError.hostOnlyUnsupported because Apple's Virtualization.framework does not currently provide a host-only network attachment API.

Validation and Error Handling

The networking subsystem implements strict validation to prevent misconfiguration:

  • bridgeInterfaceNotFound – Thrown when the user specifies a bridge interface that availableBridgeInterfaces() cannot locate
  • noBridgeInterfaces – Thrown when the host system has no VZBridgedNetworkInterface instances available
  • bridgeInterfaceWithoutBridgedMode – Thrown when --bridge-interface is provided without explicitly setting --network bridged

All errors conform to CustomStringConvertible to provide human-readable messages in the CLI output (defined in VPhoneNetworking.swift lines 6-17).

Runtime Configuration Flow

The declarative configuration follows this execution path:

  1. CLI Invocation – User executes vphone vm config myVM --network bridged --bridge-interface en0
  2. Option Parsing – VPhoneVMCLI.swift converts flags to NetworkMode.bridged and validates syntax
  3. Config Merge – VPhoneBundleOps.updateConfig calls VPhoneNetworking.merge() to reconcile new settings with the existing manifest
  4. Persistence – The updated NetworkConfig is written to the VM bundle's manifest
  5. Boot Injection – When the VM starts, VPhoneVirtualMachine.init(options:) loads the manifest and calls makeNetworkDevice()
  6. Device Attachment – The resulting NIC (or nil) is assigned to config.networkDevices before the VM launches

Practical Code Examples

Creating a NAT configuration:

import VPhoneCore

let config = VPhoneVirtualMachineManifest.NetworkConfig(mode: .nat, macAddress: "")
let device = try VPhoneNetworking.makeNetworkDevice(config)
// Returns VZVirtioNetworkDeviceConfiguration configured with VZNATNetworkDeviceAttachment

Configuring bridged networking on a specific interface:

let bridgedConfig = try VPhoneNetworking.merge(
    into: VPhoneVirtualMachineManifest.NetworkConfig.default,
    mode: .bridged,
    bridgeInterface: "en0"
)
let device = try VPhoneNetworking.makeNetworkDevice(bridgedConfig)
// Returns device configured with VZBridgedNetworkDeviceAttachment

Disabling network access entirely:

let offConfig = VPhoneVirtualMachineManifest.NetworkConfig(mode: .off, macAddress: "")
let device = try VPhoneNetworking.makeNetworkDevice(offConfig)
// Returns nil; config.networkDevices will be empty

Summary

  • vphone-cli implements networking through three components: VPhoneNetworking for validation, VPhoneVirtualMachine for runtime injection, and VPhoneVMCLI for user interaction
  • Three modes are supported: NAT (default via VZNATNetworkDeviceAttachment), bridged (via VZBridgedNetworkDeviceAttachment), and disabled
  • Validation occurs centrally in VPhoneNetworking.swift via merge() and makeNetworkDevice(), ensuring CLI configuration and VM boot use identical logic
  • Host-only networking is unsupported due to Virtualization.framework API limitations
  • File locations: Core logic resides in sources/VPhoneCore/VPhoneNetworking.swift and sources/vphone-cli/VPhoneVirtualMachine.swift

Frequently Asked Questions

What network modes does vphone-cli support?

vphone-cli supports NAT (the default), bridged, and disabled networking modes. NAT uses macOS internet sharing to route traffic, bridged mode attaches the VM directly to a physical host interface like en0, and disabled mode removes all networking hardware. Host-only networking is explicitly unsupported; attempting to configure it triggers VPhoneNetworkingError.hostOnlyUnsupported because Apple's Virtualization.framework lacks the necessary attachment type.

How does vphone-cli validate bridge interfaces?

The framework validates interfaces through VPhoneNetworking.availableBridgeInterfaces(), which queries VZBridgedNetworkInterface.networkInterfaces to enumerate host bridges. If the specified interface is missing, the code throws bridgeInterfaceNotFound. If the system has no bridge-capable interfaces, it throws noBridgeInterfaces. The parser also enforces that --bridge-interface can only be used when --network bridged is explicitly set, throwing bridgeInterfaceWithoutBridgedMode otherwise.

Where is the network device actually attached to the VM?

The attachment occurs in sources/vphone-cli/VPhoneVirtualMachine.swift during the VM initialization sequence. After loading the manifest's networkConfig, the initializer calls VPhoneNetworking.makeNetworkDevice() and appends the result to the VZVirtualMachineConfiguration.networkDevices array. This happens before the virtual machine is instantiated, ensuring the network hardware is present at boot time.

Can I change networking settings while the VM is running?

No. vphone-cli uses a declarative configuration model where settings are persisted to the VM's manifest file. Network configuration is read once during VPhoneVirtualMachine initialization, and Apple's Virtualization.framework does not support hot-plugging network devices after the VM has started. To change networking, you must stop the VM, run vphone vm config to update the manifest, and then start the VM again.

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 →