Manifest-Driven Providers
vx uses a declarative manifest system to define tools and their behaviors. Instead of writing Rust code for each tool, you can simply create a provider.toml file that describes everything vx needs to know about a tool.
Overview
A manifest-driven provider is defined by a single provider.toml file that contains:
- Provider metadata: Name, description, homepage, ecosystem
- Runtime definitions: Executables, aliases, version sources
- Platform configurations: OS-specific settings, download URLs
- Dependencies: What other tools are required or recommended
- Detection rules: How to find existing installations
This approach makes it easy to:
- Add new tools without writing code
- Customize tool behavior through configuration
- Share tool definitions across teams
- Maintain consistent tool management
Quick Start
Using Built-in Providers
vx comes with 40+ built-in providers for popular tools. Just use them directly:
# These tools are already defined in vx
vx node --version # Node.js
vx go version # Go
vx jq --help # jq JSON processor
vx ffmpeg -version # FFmpeg media toolkitCreating a Custom Provider
To add a new tool, create a provider.toml file:
# Create provider directory
mkdir -p ~/.vx/providers/mytool
# Create the manifest
cat > ~/.vx/providers/mytool/provider.toml << 'EOF'
[provider]
name = "mytool"
description = "My awesome tool"
homepage = "https://mytool.example.com"
ecosystem = "devtools"
[[runtimes]]
name = "mytool"
description = "My tool runtime"
executable = "mytool"
[runtimes.versions]
source = "github-releases"
owner = "myorg"
repo = "mytool"
[runtimes.platforms.windows]
executable_extensions = [".exe"]
[runtimes.platforms.unix]
executable_extensions = []
EOF
# Now use it!
vx mytool --versionProvider Manifest Schema
Provider Section
The [provider] section defines basic metadata:
[provider]
name = "jq" # Required: Provider name
description = "Lightweight command-line JSON processor" # Required: Description
homepage = "https://jqlang.github.io/jq/" # Optional: Homepage URL
repository = "https://github.com/jqlang/jq" # Optional: Source repository
ecosystem = "devtools" # Optional: Ecosystem categoryEcosystem values:
nodejs- Node.js ecosystem (npm, yarn, etc.)python- Python ecosystem (pip, uv, etc.)rust- Rust ecosystem (cargo, etc.)go- Go ecosystemsystem- System tools (ffmpeg, git, etc.)devtools- Development tools (jq, fzf, etc.)cloud- Cloud CLI tools (aws, gcloud, etc.)
Runtime Section
Each [[runtimes]] entry defines an executable:
[[runtimes]]
name = "jq" # Required: Runtime name
description = "jq JSON processor" # Required: Description
executable = "jq" # Required: Executable name
aliases = ["jqp"] # Optional: Alternative names
priority = 100 # Optional: Priority (higher = preferred)
auto_installable = true # Optional: Can be auto-installed (default: true)
bundled_with = "node" # Optional: Bundled with another runtimeVersion Sources
The [runtimes.versions] section defines where to get version information:
GitHub Releases
[runtimes.versions]
source = "github-releases"
owner = "jqlang"
repo = "jq"
strip_v_prefix = true # Remove 'v' from version tagsNode.js Official
[runtimes.versions]
source = "nodejs-org"
lts_pattern = "lts/*"System Detection
[runtimes.versions]
source = "system" # Detect from system installationPlatform Configuration
Define platform-specific settings:
[runtimes.platforms.windows]
executable_extensions = [".exe", ".cmd"]
search_paths = ["C:\\Program Files\\tool\\bin"]
[runtimes.platforms.unix]
executable_extensions = []
search_paths = ["/usr/bin", "/usr/local/bin"]
[runtimes.platforms.linux]
executable_extensions = []
[runtimes.platforms.macos]
executable_extensions = []Download Layout
Configure how downloads are structured:
Binary Downloads
For single-file executables:
[runtimes.layout]
download_type = "binary"
[runtimes.layout.binary."windows-x86_64"]
source_name = "jq-windows-amd64.exe"
target_name = "jq.exe"
target_dir = "bin"
[runtimes.layout.binary."linux-x86_64"]
source_name = "jq-linux-amd64"
target_name = "jq"
target_dir = "bin"
target_permissions = "755"Archive Downloads
For tools distributed as archives:
[runtimes.layout]
download_type = "archive"
[runtimes.layout.archive]
strip_prefix = "node-v{version}-{platform}-{arch}"
executable_paths = [
"bin/node.exe", # Windows
"bin/node" # Unix
]Detection Configuration
Define how to detect existing installations:
[runtimes.detection]
command = "{executable} --version"
pattern = "v?(\\d+\\.\\d+\\.\\d+)"
system_paths = [
"/usr/bin/node",
"/usr/local/bin/node",
"C:\\Program Files\\nodejs\\node.exe"
]
env_hints = ["NODE_HOME", "NVM_DIR"]Dependencies and Constraints
Define relationships between tools:
# Recommend npm when using node
[[runtimes.constraints]]
when = "*"
recommends = [
{ runtime = "npm", version = "*", reason = "Default package manager" }
]
# Require node for npm
[[runtimes.constraints]]
when = "*"
requires = [
{ runtime = "node", version = ">=14", recommended = "20", reason = "npm requires Node.js" }
]
# Version-specific constraints
[[runtimes.constraints]]
when = ">=9"
requires = [
{ runtime = "node", version = ">=14", reason = "npm 9.x+ requires Node.js 14+" }
]Environment Variables
Configure environment variables:
[runtimes.env]
vars = { PATH = "{install_dir}/bin" }
# Version-conditional environment
[runtimes.env.conditional]
">=18" = { NODE_OPTIONS = "--experimental-vm-modules" }Template Variables:
The following template variables are supported in environment variable values:
| Variable | Description | Example |
|---|---|---|
{install_dir} | Installation directory of the runtime | ~/.vx/store/python/3.11.0 |
{version} | Version of the runtime | 3.11.0 |
{executable} | Path to the executable | ~/.vx/store/python/3.11.0/bin/python |
These variables are automatically expanded when the runtime is executed.
Mirror Configuration
Define download mirrors for different regions:
[[runtimes.mirrors]]
name = "taobao"
region = "cn"
url = "https://npmmirror.com/mirrors/node"
priority = 100
[[runtimes.mirrors]]
name = "ustc"
region = "cn"
url = "https://mirrors.ustc.edu.cn/node"
priority = 90Health Checks
Define health check commands:
[runtimes.health]
check_command = "{executable} --version"
expected_pattern = "v\\d+\\.\\d+\\.\\d+"
exit_code = 0
timeout_ms = 5000
check_on = ["install", "activate"]Download Configuration
Configure download behavior:
[runtimes.download]
timeout_ms = 900000 # 15 minutes for large files
max_retries = 5
resume_enabled = true
execution_timeout_ms = 60000 # 1 minute execution timeoutTest Configuration
Define automated tests for the provider:
[runtimes.test]
timeout_ms = 30000
functional_commands = [
{ command = "{executable} --version", expect_success = true, expected_output = "v\\d+", name = "version_check" },
{ command = "{executable} -e \"console.log('test')\"", expect_success = true, expected_output = "test", name = "eval_test" }
]
install_verification = [
{ command = "{executable} --version", expect_success = true }
]Real-World Examples
Simple Binary Tool (jq)
[provider]
name = "jq"
description = "Lightweight and flexible command-line JSON processor"
homepage = "https://jqlang.github.io/jq/"
repository = "https://github.com/jqlang/jq"
ecosystem = "devtools"
[[runtimes]]
name = "jq"
description = "jq - command-line JSON processor"
executable = "jq"
[runtimes.versions]
source = "github-releases"
owner = "jqlang"
repo = "jq"
strip_v_prefix = true
[runtimes.layout]
download_type = "binary"
[runtimes.layout.binary."windows-x86_64"]
source_name = "jq-windows-amd64.exe"
target_name = "jq.exe"
target_dir = "bin"
[runtimes.layout.binary."linux-x86_64"]
source_name = "jq-linux-amd64"
target_name = "jq"
target_dir = "bin"
target_permissions = "755"
[runtimes.layout.binary."macos-x86_64"]
source_name = "jq-macos-amd64"
target_name = "jq"
target_dir = "bin"
target_permissions = "755"
[runtimes.platforms.windows]
executable_extensions = [".exe"]
[runtimes.platforms.unix]
executable_extensions = []
[[runtimes.constraints]]
when = "*"
recommends = []Multi-Runtime Provider (Node.js)
[provider]
name = "node"
description = "JavaScript runtime built on Chrome's V8 engine"
homepage = "https://nodejs.org"
ecosystem = "nodejs"
# Main runtime
[[runtimes]]
name = "node"
description = "Node.js runtime"
executable = "node"
aliases = ["nodejs"]
priority = 100
auto_installable = true
[runtimes.versions]
source = "nodejs-org"
lts_pattern = "lts/*"
[runtimes.layout]
download_type = "archive"
[runtimes.layout.archive]
strip_prefix = "node-v{version}-{platform}-{arch}"
executable_paths = ["bin/node.exe", "bin/node"]
[[runtimes.constraints]]
when = "*"
recommends = [
{ runtime = "npm", version = "*", reason = "Default package manager" }
]
# Bundled npm
[[runtimes]]
name = "npm"
description = "Node Package Manager"
executable = "npm"
bundled_with = "node"
[runtimes.platforms.windows]
executable_extensions = [".cmd", ".exe"]
[runtimes.platforms.unix]
executable_extensions = []
[[runtimes.constraints]]
when = ">=9"
requires = [
{ runtime = "node", version = ">=14", reason = "npm 9.x+ requires Node.js 14+" }
]
# Bundled npx
[[runtimes]]
name = "npx"
description = "Node Package Execute"
executable = "npx"
bundled_with = "node"
[runtimes.platforms.windows]
executable_extensions = [".cmd", ".exe"]
[runtimes.platforms.unix]
executable_extensions = []System Tool (systemctl)
[provider]
name = "systemctl"
description = "systemd system and service manager"
homepage = "https://systemd.io"
ecosystem = "system"
# Platform restriction
[provider.platforms]
os = ["linux"]
[[runtimes]]
name = "systemctl"
description = "Control systemd services and units"
executable = "systemctl"
auto_installable = false # Cannot be auto-installed
[runtimes.versions]
source = "system"
[runtimes.detection]
command = "{executable} --version"
pattern = "systemd ([\\d.]+)"
system_paths = ["/usr/bin/systemctl", "/bin/systemctl"]
[runtimes.platforms.linux]
executable_extensions = []
search_paths = ["/usr/bin", "/bin"]
# Bundled tools
[[runtimes]]
name = "journalctl"
description = "View systemd journal logs"
executable = "journalctl"
bundled_with = "systemctl"
auto_installable = false
[[runtimes.constraints]]
when = "*"
requires = [
{ runtime = "systemctl", version = "*", reason = "journalctl is part of systemd" }
]Provider Directory Structure
vx loads providers from multiple locations:
~/.vx/providers/ # User-defined providers (highest priority)
├── mytool/
│ └── provider.toml
└── custom-node/
└── provider.toml
$VX_PROVIDERS_PATH/ # Environment variable path
└── team-tools/
└── provider.toml
Built-in providers # Lowest priorityLoading Priority:
~/.vx/providers/*/provider.toml(user local, highest)$VX_PROVIDERS_PATH/*/provider.toml(environment variable)- Built-in providers (lowest)
Best Practices
1. Use Descriptive Names
# Good
name = "ripgrep"
description = "Fast line-oriented search tool, recursively searches directories"
# Avoid
name = "rg"
description = "Search tool"2. Define All Platforms
# Support all major platforms
[runtimes.layout.binary."windows-x86_64"]
source_name = "tool-windows-amd64.exe"
[runtimes.layout.binary."linux-x86_64"]
source_name = "tool-linux-amd64"
[runtimes.layout.binary."linux-aarch64"]
source_name = "tool-linux-arm64"
[runtimes.layout.binary."macos-x86_64"]
source_name = "tool-darwin-amd64"
[runtimes.layout.binary."macos-aarch64"]
source_name = "tool-darwin-arm64"3. Set Appropriate Timeouts
# Small tools (< 10MB)
[runtimes.download]
timeout_ms = 60000 # 1 minute
# Large tools (> 100MB like FFmpeg)
[runtimes.download]
timeout_ms = 900000 # 15 minutes
resume_enabled = true4. Document Dependencies
[[runtimes.constraints]]
when = "*"
requires = [
{ runtime = "node", version = ">=14", reason = "Requires Node.js 14+ for ES modules support" }
]
recommends = [
{ runtime = "npm", version = "*", reason = "Recommended for package management" }
]5. Add Health Checks
[runtimes.health]
check_command = "{executable} --version"
expected_pattern = "\\d+\\.\\d+\\.\\d+"
timeout_ms = 5000Troubleshooting
Provider Not Found
# Check if provider is loaded
vx list
# Verify provider.toml location
ls ~/.vx/providers/mytool/provider.tomlVersion Detection Fails
# Test detection pattern manually
mytool --version
# Check detection configuration
cat ~/.vx/providers/mytool/provider.toml | grep -A5 "\[runtimes.detection\]"Download Fails
- Check network connectivity
- Verify download URL format in manifest
- Try increasing timeout:toml
[runtimes.download] timeout_ms = 300000 max_retries = 5
See Also
- Provider Development Guide - For Rust-based providers
- Configuration Reference - Project configuration
- CLI Commands - Command reference