Override Precedence for `VPHONE_ROOT`, `VPHONE_LIBRARY_ROOT`, and `VPHONE_VENV_DIR` in vphone-cli

In vphone-cli, VPHONE_LIBRARY_ROOT and VPHONE_VENV_DIR take precedence over VPHONE_ROOT, while VPHONE_ROOT only affects paths when the specific per-item variables are unset.

The open-source virtualization tool vphone-cli stores per-user data under ~/.vphone/ by default, but three environment variables allow you to relocate specific directories. Understanding the override precedence for VPHONE_ROOT, VPHONE_LIBRARY_ROOT, and VPHONE_VENV_DIR ensures you can configure storage locations without unexpected conflicts when managing VM libraries and Python runtimes.

The Hierarchy of Environment Variable Overrides

The precedence rules follow a specific cascade designed to allow fine-grained control without moving the entire data tree unnecessarily.

Per-Item Overrides (Highest Priority)

VPHONE_LIBRARY_ROOT and VPHONE_VENV_DIR represent the highest level of override precedence. When either variable is defined, it wins regardless of whether VPHONE_ROOT is set.

  • VPHONE_LIBRARY_ROOT: Redirects the VM library directory (default: ~/.vphone/VMs)
  • VPHONE_VENV_DIR: Redirects the auto-provisioned Python virtual environment (default: ~/.vphone/venv)

Root Override (Secondary Priority)

VPHONE_ROOT acts as a fallback that relocates the entire ~/.vphone tree. It only takes effect when both VPHONE_LIBRARY_ROOT and VPHONE_VENV_DIR are undefined. When active, it moves the library, venv, and auxiliary caches (ipsws/, tools/, debs/) to the specified location.

Default Locations (Base Case)

If none of the three environment variables are defined, vphone-cli uses the built-in defaults:

  • Root: ~/.vphone/
  • Library: ~/.vphone/VMs/
  • Virtual Environment: ~/.vphone/venv/

Source Code Implementation

The override logic is hardcoded in the Swift source files of the Lakr233/vphone-cli repository.

VM Library Resolution in VPhoneLibrary.swift

In sources/VPhoneCore/VPhoneLibrary.swift, the defaultRoot() method implements the precedence check:

public static func defaultRoot() -> URL {
    if let override = ProcessInfo.processInfo.environment["VPHONE_LIBRARY_ROOT"] {
        return URL(fileURLWithPath: override, isDirectory: true)
    }
    if let root = ProcessInfo.processInfo.environment["VPHONE_ROOT"] {
        return URL(fileURLWithPath: root, isDirectory: true)
            .appendingPathComponent("VMs", isDirectory: true)
    }
    return VPhoneResources.userDataRoot()
        .appendingPathComponent("VMs", isDirectory: true)
}

This function checks VPHONE_LIBRARY_ROOT first, then falls back to constructing a path from VPHONE_ROOT, and finally uses the default user data location.

Python Virtual Environment Resolution in VPhoneResources.swift

In sources/VPhoneCore/VPhoneResources.swift, the venvDirectory property handles VPHONE_VENV_DIR:

public static var venvDirectory: URL {
    if let dir = ProcessInfo.processInfo.environment["VPHONE_VENV_DIR"], !dir.isEmpty {
        return URL(fileURLWithPath: dir, isDirectory: true)
    }
    let root = ProcessInfo.processInfo.environment["VPHONE_ROOT"]
        ?? FileManager.default.homeDirectoryForCurrentUser
            .appendingPathComponent(".vphone", isDirectory: true).path
    return URL(fileURLWithPath: root, isDirectory: true)
        .appendingPathComponent("venv", isDirectory: true)
}

Notice that VPHONE_VENV_DIR bypasses the root variable entirely, while the fallback chain uses VPHONE_ROOT before defaulting to ~/.vphone/.

Practical Configuration Examples

These shell examples demonstrate the precedence rules in action.

Redirect only the VM library while keeping other data in the default location:

export VPHONE_LIBRARY_ROOT=/data/vphone_lib
vphone-cli vm list  # Uses /data/vphone_lib despite VPHONE_ROOT being unset

Override the Python environment regardless of root settings:

export VPHONE_ROOT=/tmp/vphone_root
export VPHONE_VENV_DIR=/opt/vphone_venv
vphone-cli python-runtime  # Uses /opt/vphone_venv, not /tmp/vphone_root/venv

Move the entire tree using only VPHONE_ROOT:

export VPHONE_ROOT=/mnt/external/vphone
vphone-cli vm list  # Uses /mnt/external/vphone/VMs

vphone-cli python-runtime  # Uses /mnt/external/vphone/venv

Summary

  • Per-item variables win: VPHONE_LIBRARY_ROOT and VPHONE_VENV_DIR always take precedence over VPHONE_ROOT.
  • Root is a fallback: VPHONE_ROOT only affects the library and venv locations when the specific overrides are absent.
  • Implementation clarity: The precedence logic is explicitly coded in sources/VPhoneCore/VPhoneLibrary.swift and sources/VPhoneCore/VPhoneResources.swift, ensuring consistent behavior across the CLI.

Frequently Asked Questions

What happens if I set both VPHONE_ROOT and VPHONE_LIBRARY_ROOT?

VPHONE_LIBRARY_ROOT takes precedence. The VM library will be stored at the location specified by VPHONE_LIBRARY_ROOT, while other directories (like ipsws/, tools/, and venv/) will respect VPHONE_ROOT or fall back to defaults.

Can I use VPHONE_ROOT to move just the Python virtual environment?

No. To relocate only the Python virtual environment, you must set VPHONE_VENV_DIR explicitly. VPHONE_ROOT moves the venv only when VPHONE_VENV_DIR is undefined.

Does vphone-cli create these directories automatically if they don't exist?

Yes. According to the source implementation in VPhoneLibrary.swift and VPhoneResources.swift, the tool creates the necessary directory structure when initializing components, provided the parent paths exist and permissions allow.

Are these environment variables supported on all platforms?

The variables are resolved using ProcessInfo.processInfo.environment, which works across macOS and Linux platforms where vphone-cli operates. The path construction uses FileManager APIs that handle platform-specific path separators correctly.

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 →