This is a GitHub template for building a mise backend plugin using the vfox-style backend architecture.
Backend plugins in mise extend the standard tool plugin system to manage multiple tools using the plugin:tool format. They're perfect for:
- Package managers (npm, pip, cargo, gem)
- Tool families (multiple related tools from one ecosystem)
- Custom installations that need to manage many tools
Unlike tool plugins that manage one tool, backend plugins can install and manage multiple tools like npm:prettier, npm:eslint, cargo:ripgrep, etc.
- Click "Use this template" button on GitHub
- Name your repository (e.g.,
mise-mybackendorvfox-mybackend) - Clone your new repository
- Follow the setup instructions below
git clone https://github.com/jdx/mise-backend-plugin-template mise-mybackend
cd mise-mybackend
rm -rf .git
git initSearch and replace these placeholders throughout the project:
<BACKEND>→ your backend name (e.g.,npm,cargo,pip)<GITHUB_USER>→ your GitHub username or organization<TEST_TOOL>→ a real tool name your backend can install (for testing)
Files to update:
metadata.lua- Update name, description, author, homepagehooks/*.lua- Replace placeholders and implement your backend logicmise-tasks/test- Update test tool name and commandsREADME.md- Update this file with your backend's information
Backend plugins require three main hooks:
Lists available versions for a tool in your backend.
function PLUGIN:BackendListVersions(ctx)
local tool = ctx.tool
-- Your logic to fetch versions for the tool
-- Return: {versions = {"1.0.0", "1.1.0", "2.0.0"}}
endExamples:
- API-based: Query npm registry, PyPI, crates.io APIs
- Command-based: Run
npm view <tool> versions,pip index versions <tool> - File-based: Parse registry files or manifests
Installs a specific version of a tool.
function PLUGIN:BackendInstall(ctx)
local tool = ctx.tool
local version = ctx.version
local install_path = ctx.install_path
-- Your logic to install the tool
-- Return: {}
endExamples:
- Package manager:
npm install <tool>@<version>,pip install <tool>==<version> - Download & extract: Download binary/archive and extract to install_path
- Build from source: Clone repository, checkout version, build and install
Sets up environment variables for a tool.
function PLUGIN:BackendExecEnv(ctx)
local install_path = ctx.install_path
-- Your logic to set up environment
-- Return: {env_vars = {{key = "PATH", value = install_path .. "/bin"}}}
endExamples:
- Basic: Add
bin/directory to PATH - Complex: Set tool-specific environment variables, library paths
- Ecosystem-specific: Like
node_modules/.binfor npm, site-packages for Python
Your backend may need to handle different operating systems:
-- Available in all hooks via RUNTIME object
if RUNTIME.osType == "Darwin" then
-- macOS-specific logic
elseif RUNTIME.osType == "Linux" then
-- Linux-specific logic
elseif RUNTIME.osType == "Windows" then
-- Windows-specific logic
endProvide meaningful error messages:
function PLUGIN:BackendListVersions(ctx)
local tool = ctx.tool
if not tool or tool == "" then
error("Tool name cannot be empty")
end
-- ... your implementation ...
if #versions == 0 then
error("No versions found for " .. tool)
end
return {versions = versions}
end- Install pre-commit hooks (optional but recommended):
hk installThis sets up automatic linting and formatting on git commits.
- Link your plugin for development:
mise plugin link --force <BACKEND> .- Test version listing:
mise ls-remote <BACKEND>:<some-tool>- Test installation:
mise install <BACKEND>:<some-tool>@latest- Test execution:
mise exec <BACKEND>:<some-tool>@latest -- <some-tool> --version- Run tests:
mise run test- Run linting:
mise run lint- Run full CI suite:
mise run ciThis template uses hk for modern linting and pre-commit hooks:
- Automatic formatting:
styluaformats Lua code - Static analysis:
luacheckcatches Lua issues - GitHub Actions linting:
actionlintvalidates workflows - Pre-commit hooks: Runs all checks automatically on git commit
Manual commands:
hk check # Run all linters (same as mise run lint)
hk fix # Run linters and auto-fix issuesEnable debug output:
mise --debug install <BACKEND>:<tool>@<version>metadata.lua– Backend plugin metadata and configurationhooks/backend_list_versions.lua– Lists available versions for toolshooks/backend_install.lua– Installs specific versions of toolshooks/backend_exec_env.lua– Sets up environment variables for tools.github/workflows/ci.yml– GitHub Actions CI/CD pipelinemise.toml– Development tools and configurationmise-tasks/– Task scripts for testinghk.pkl– Modern linting and pre-commit hook configuration.luacheckrc– Lua linting configurationstylua.toml– Lua formatting configuration
-- backend_list_versions.lua
function PLUGIN:BackendListVersions(ctx)
local cmd = require("cmd")
local json = require("json")
local result = cmd.exec("mypm view " .. ctx.tool .. " versions --json")
return {versions = json.decode(result)}
end
-- backend_install.lua
function PLUGIN:BackendInstall(ctx)
local cmd = require("cmd")
cmd.exec("mypm install " .. ctx.tool .. "@" .. ctx.version .. " --prefix " .. ctx.install_path)
return {}
end
-- backend_exec_env.lua
function PLUGIN:BackendExecEnv(ctx)
return {
env_vars = {
{key = "PATH", value = ctx.install_path .. "/bin"}
}
}
end-- backend_list_versions.lua
function PLUGIN:BackendListVersions(ctx)
local http = require("http")
local json = require("json")
local resp = http.get({url = "https://api.github.com/repos/owner/" .. ctx.tool .. "/releases"})
local releases = json.decode(resp.body)
local versions = {}
for _, release in ipairs(releases) do
table.insert(versions, release.tag_name:gsub("^v", ""))
end
return {versions = versions}
end
-- backend_install.lua
function PLUGIN:BackendInstall(ctx)
local platform = RUNTIME.osType:lower()
local arch = RUNTIME.archType
local url = "https://github.com/owner/" .. ctx.tool .. "/releases/download/v" .. ctx.version ..
"/" .. ctx.tool .. "-" .. platform .. "-" .. arch .. ".tar.gz"
local http = require("http")
local temp_file = ctx.install_path .. "/tool.tar.gz"
http.download({url = url, output = temp_file})
local cmd = require("cmd")
cmd.exec("cd " .. ctx.install_path .. " && tar -xzf tool.tar.gz")
cmd.exec("rm " .. temp_file)
return {}
end- vfox-npm - Backend for npm packages
- Study existing mise backends: npm, cargo, pip, gem
| Variable | Type | Description | Example |
|---|---|---|---|
ctx.tool |
string | Tool name | "prettier" |
| Variable | Type | Description | Example |
|---|---|---|---|
ctx.tool |
string | Tool name | "prettier" |
ctx.version |
string | Tool version | "3.0.0" |
ctx.install_path |
string | Installation directory | "/home/user/.local/share/mise/installs/npm/prettier/3.0.0" |
Backend plugins have access to these built-in modules:
cmd- Execute shell commandshttp- HTTP client for downloads and API callsjson- JSON parsing and encodingfile- File system operations
- Ensure all tests pass:
mise run ci - Create a GitHub repository for your plugin
- Push your code
- Test with:
mise plugin install mybackend https://github.com/user/mise-mybackend - (Optional) Request to transfer to mise-plugins organization
- Add to the mise registry via PR
- Backend Plugin Development - Complete guide
- Backend Architecture - How backends work
- Lua modules reference - Available modules
- mise-plugins organization - Community plugins
MIT