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

> Explore how vphone-cli manages virtual machine networking with Virtualization.framework supporting NAT, bridged, and disabled modes via a declarative manifest.

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

---

**vphone-cli configures virtual machine networking using Apple's Virtualization.framework through a declarative manifest system that validates settings in [`VPhoneNetworking.swift`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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:**

```swift
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:**

```swift
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:**

```swift
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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/sources/VPhoneCore/VPhoneNetworking.swift) and [`sources/vphone-cli/VPhoneVirtualMachine.swift`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/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.