The zmax plugin package manager

July 24, 2026 · View on GitHub

zmax has a built-in package manager for its native (compiled Rust) plugins — the cdylibs built against the zmax-native SDK. Helix, which zmax forks, ships no plugin system at all; zmax installs a third-party compiled plugin from owner/repo into a content-addressed global store and loads it at runtime by mmap (dlopen), with SHA-256 integrity pinning and zero editor recompile.

It is global only: one store under ~/.zmax/pkg/, no per-project manifest or lockfile. The whole workflow is one line per plugin in your config:

:zmax-native get owner/repo

On first launch that installs the plugin and loads it; every launch after, the same line loads it from the store with no network. :zmax-native needs git on PATH for remote sources and cargo for plugins shipped as source (no prebuilt cdylib).

Ported from zshrs's znative package manager, reduced to zmax's single plugin kind (native) — an editor has no shell to source script plugins into.

Commands

All package-manager verbs are subcommands of :zmax-native (aliased :plugin).

Command (aliases)ArgumentsWhat it does
add (install, i)SOURCE…Resolve, build if needed, install into the store, record in the index, and load. Reinstalls (force) if already present. Multiple sources allowed.
get (ensure)NAME_or_SOURCE…Load from the store, installing on first use — zero network once stored. This is the config startup line.
syncLoad every installed plugin (config startup, no arguments).
remove (rm, uninstall)NAME…Unload, delete the store copy, drop the index row.
registry (installed)List installed plugins: name version source.
info (show)NAMEFull record: name, version, source, store path, cdylib, integrity.
update (upgrade, up)[NAME]Re-resolve and reinstall from the recorded source (one, or all) — pulls latest upstream and rebuilds.
gc[--dry-run|-n]Remove store/<name>@<version>/ directories not pinned by the index (orphans from upgrades) plus the git/ clone cache.
cleanClear the scratch directories (git/, cache/, bin/); the store and index are untouched.

Raw host verbs (not the package manager, kept for direct use):

CommandArgumentsWhat it does
loadPATH…dlopen a cdylib by file path directly (no store, no index).
unloadNAME…Unload a loaded plugin from memory (does not delete it).
listList the currently loaded plugins (vs registry, which lists installed).

Errors surface in the editor status line as plugin: <reason>.

Sources

The add/get/update spec is auto-classified:

FormExampleResolves to
owner/repoMenkeTechnologies/zmax-hellogit clone https://github.com/owner/repo
github:owner/repogithub:owner/repoGitHub clone (explicit)
git+URLgit+https://gitlab.com/team/plug.gitgit clone URL
a URL ending .git or with ://https://example.com/x.gitgit clone URL
path:DIRpath:./examples/hello-pluginlocal directory (no network)
an absolute / ./ / ../ / ~ path~/src/my-pluginlocal directory (no network)

Install by version — any remote form may carry an @ref suffix (split after the last /) to pin a tag, branch, or commit: owner/repo@v1.2.0, git+https://host/x.git@main. The pin is recorded in the index (source = github:owner/repo@v1.2.0), so registry shows it, update re-fetches that exact ref (not HEAD), and get owner/repo@v1.2.0 matches only that pin. Clones are shallow (git clone --depth 1 [--branch REF]); an arbitrary commit sha a shallow --branch clone can't reach falls back to a full clone + git checkout.

How a plugin is built

A native plugin is loaded from a lib*.{dylib,so}. When the resolved source has no prebuilt library at its root, :zmax-native add runs cargo build --release in the staged tree and stages the produced cdylib. The crate must declare:

[lib]
crate-type = ["cdylib"]

The kind is determined from an optional zmax-native.toml first, then by layout:

  1. an explicit [native] table in zmax-native.toml, or
  2. a prebuilt lib*.{dylib,so} at the tree root, or
  3. a Cargo.toml whose [lib] crate-type includes cdylib.

Anything else is refused — it is not a zmax plugin.

The store

Everything lives under ~/.zmax/pkg/ (override the root with ZMAX_PKG_DIR):

~/.zmax/pkg/
  store/<name>@<version>/   # the installed plugin (content-addressed)
  installed.toml            # the global index — the source of truth
  git/                      # scratch: remote clones land here, then copy to store/
  cache/  bin/              # internal scratch

The copy into store/ excludes .git/ and target/, so the store holds only loadable content. Each install is SHA-256 pinned as sha256-<hex> in installed.toml. A record:

[[package]]
name = "hello"
version = "0.1.0"
source = "github:owner/repo"
kind = "native"
integrity = "sha256-…"
lib = "libhello.dylib"   # the cdylib that :zmax-native load dlopens (mmap)

zmax-native.toml (optional manifest)

A plugin repo may ship a zmax-native.toml at its root to declare metadata and the build recipe explicitly (it overrides auto-detection):

[plugin]
name = "hello"
version = "0.1.0"
description = "example zmax plugin"

[native]
lib = "hello"      # produces lib<lib>.{dylib,so}
# build = true     # run `cargo build`; defaults to true when a Cargo.toml is
                   # present and no prebuilt cdylib sits at the tree root

An ordinary cdylib crate needs no zmax-native.toml at all.

In your config

zmax sources two of its own init files at startup — always, no opt-in — right after the editor is built and before the UI loop. List the plugins you want with the get verb (install-on-first-use, then zero-network on every later launch), one per line, in whichever you prefer:

~/.zmax/init.vim — vim ex-command syntax. The leading : is required: a bare command word is parsed by vimlrs as a vimscript expression, not an ex-command, so it never reaches zmax's command dispatcher.

:zmax-native get MenkeTechnologies/zmax-native-wc
:zmax-native get MenkeTechnologies/zmax-native-uuid
:zmax-native get MenkeTechnologies/zmax-native-toc

~/.zmax/init.el — Emacs Lisp syntax (sourced before init.vim if both exist):

(editor-command "zmax-native" "get" "MenkeTechnologies/zmax-native-wc")
(editor-command "zmax-native" "get" "MenkeTechnologies/zmax-native-lorem")

Both go through the same dispatcher that resolves interactive : commands, at a single point with the full command system live — so the lines run neither too early nor too late.

Use get, not add: get loads from the store and installs only on the first launch, whereas add force-reinstalls (re-clone + rebuild) on every startup. A bare :zmax-native sync loads everything already in the store — handy if you prefer to :zmax-native add interactively and keep just one sync line.

Your personal ~/.vimrc / ~/.emacs.d/init.el are also sourced, but only when opted in via ~/.zmax/config.toml:

[editor]
source-vimrc = true          # source ~/.vimrc, ~/.vim/vimrc, ~/.config/nvim/init.vim
source-emacs-config = true   # source ~/.emacs.d/init.el, ~/.config/emacs/init.el, ~/.emacs

Editor-specific plugin lines belong in ~/.zmax/init.vim / ~/.zmax/init.el, not a shared vim/emacs config.

Examples

:zmax-native add path:./zmax-native/examples/hello-plugin   # local checkout
:zmax-native add owner/repo                                 # install (force) + load
:zmax-native add owner/repo@v0.2.1                          # pinned ref
:zmax-native get owner/repo                                 # install-on-first-use, else load
:zmax-native registry                                       # what's installed
:zmax-native info hello                                     # details for one
:zmax-native update                                         # reinstall everything from source
:zmax-native gc --dry-run                                   # what gc would reclaim
:zmax-native remove hello                                   # unload + delete

Published example plugins

Installable standalone plugin repos, each an ordinary cdylib crate depending on the zmax-native SDK — the shape a third-party plugin takes:

RepoCommand(s)What it does
MenkeTechnologies/zmax-native-wc:wcline/word/char/byte counts of the buffer on the status line
MenkeTechnologies/zmax-native-uuid:uuid, :uuid-upperinsert a random UUIDv4 at the cursor
MenkeTechnologies/zmax-native-toc:tocinsert a Markdown table of contents from the buffer's headings
MenkeTechnologies/zmax-native-lorem:lorem [n]insert n paragraphs of lorem-ipsum placeholder text
:zmax-native add MenkeTechnologies/zmax-native-wc
:zmax-native add MenkeTechnologies/zmax-native-uuid
:zmax-native add MenkeTechnologies/zmax-native-toc

The buildable, in-tree SDK examples live under zmax-native/examples/ (hello, insert-date, buffer-stats, trim-trailing, banner).