VPM
The vpm command-line tool for the VOLT plugin registry, which packs, publishes, deprecates, searches and installs plugin bundles.
vpm is the command-line client of the VOLT plugin registry at https://registry.voltcloud.dev. You use it to publish a plugin's platform bundles, to deprecate a version, and to search, inspect and download packages into the local plugin cache. The marketplace in VOLT reads the same registry. Its source is VoltLabs-Research/vpm.
For how a plugin and its bundles are built, see Writing plugins.
Install
vpm needs Node.js 20 or later.
npm install -g @voltstack/vpm
vpm --versionThis page documents the npm release, 1.0.2. The repository's main branch adds two commands, vpm version and vpm seal, and a check in vpm publish; run npm run build in a checkout to use them.
Sign in
vpm loginvpm login uses a device-code flow: it prints a URL and a code, opens the URL in your browser (pass --no-browser to skip that), and waits until you confirm the code there. It then stores the tokens and prints the account you signed in as.
| Command | What it does |
|---|---|
vpm login [--no-browser] | Signs in with the device-code flow. |
vpm whoami | Prints the account ID, email and username of the stored session. |
vpm logout [--keep-remote] | Revokes the session's refresh token on the console, unless --keep-remote, and deletes the stored credentials. |
Credentials are kept in the operating system's keychain (service voltcloud, account default). When no keychain is available, or when VPM_NO_KEYRING is 1 or true, they go to $XDG_CONFIG_HOME/vpm/credentials (~/.config/vpm/credentials), readable only by you.
Personal access tokens
For CI, create a personal access token and pass it with --token to vpm publish and vpm deprecate:
vpm token create --label "GitHub Actions" --scope <scope>
vpm token list
vpm token revoke <id>| Command | Options |
|---|---|
vpm token create | --label <label> (required), --scope <scope...> (one or more; read when omitted), --scope-mask <mask> (a numeric bitmask), --expires <iso> (an ISO 8601 timestamp). Prints the token once. |
vpm token list, vpm token ls | --json |
vpm token revoke <id> |
The plugin repositories' publish workflows set VPM_NO_KEYRING=1 and pass their token from the VPM_PUBLISH_TOKEN secret.
Package names and versions
A package is named @<username>/<name>. The username is lowercase letters, digits and dashes, starting with a letter or digit; the name may also contain dots and underscores. The registry answers 403 when your token may not publish under that username.
Commands that take a package accept a version after a second @:
| Form | Meaning |
|---|---|
@voltlabs/opendxa | The latest dist-tag. |
@voltlabs/opendxa@2.5.1 | That version. |
@voltlabs/opendxa@latest | A dist-tag. |
@voltlabs/opendxa@^2.0.0 | The highest version that satisfies the semver range (vpm install only). |
A version has one bundle per platform tag, such as linux-x86_64, darwin-arm64, windows-x86_64 or wasm-wasm32 (see Platforms and bundles). vpm install picks the tag of the machine it runs on: <system>-<machine>, with x64 written as x86_64.
The manifest: vpm.json
Every package has a vpm.json at its root. Commands that read it look in the given directory and then in each parent.
{
"name": "@voltlabs/coordination-analysis",
"version": "2.1.1",
"kind": "workflow",
"publisher": "voltlabs",
"description": "Analyzes local atomic coordination by computing coordination numbers, the radial distribution function (RDF), and per-type-pair partial RDF histograms.",
"license": "MIT",
"homepage": "https://github.com/voltlabs-research/coordination-analysis",
"repository": { "type": "git", "url": "https://github.com/voltlabs-research/coordination-analysis" },
"entrypoints": { "binary": "coordination-analysis-plugin.zip", "workflow": "plugin.json" },
"platforms": ["linux-x86_64", "darwin-arm64", "windows-x86_64", "wasm-wasm32"],
"coretoolkit": "2.0.0",
"files": ["plugin.json", "scripts", "bin", "lib"]
}| Field | Required | Description |
|---|---|---|
name | yes | @<username>/<name>. |
version | yes | The version to publish. Keep it equal to the plugin.json modifier's version. |
kind | yes | workflow, engine or lib. Plugins are workflow; VOLT's marketplace searches only that kind. |
publisher | yes | The publisher's name, usually the username. |
description, license, homepage, repository ({ type, url }), keywords | Package metadata. | |
entrypoints | { binary?, workflow? }: the plugin's binary and workflow files. | |
platforms | Platform tags vpm publish requires a bundle for. | |
nodeTypes, voltsdk, coretoolkit | Free-form metadata. coretoolkit records the CoreToolkit version the plugin targets. | |
files | What vpm pack puts in the tarball. |
Commands
These options apply to every command:
| Option | Description |
|---|---|
--registry <url> | Registry URL. Default: VPM_REGISTRY, else https://registry.voltcloud.dev. |
--console <url> | Console URL, which handles sign-in and tokens. Default: VPM_CONSOLE, else https://server.console.voltcloud.dev. |
--verbose | Verbose logging. |
-v, --version | Print vpm's version. |
-h, --help | Help for vpm or for a command. |
Discover
| Command | Options | What it does |
|---|---|---|
vpm search <query> | --kind <kind>, --page <n> (1), --page-size <n> (20), --json | Searches the registry and prints each package with its latest version, kind, description and download counts. |
vpm info <pkg> | --json | Without a version: the package's kind, description, homepage, dist-tags, version count and downloads. With an exact version: when and by whom it was published, its size, SHA-256, platforms and deprecation. |
Install
| Command | Options | What it does |
|---|---|---|
vpm install <pkg>, vpm i | --platform <tag>, --force | Resolves the version, downloads the bundle for the platform and extracts it to the cache. Skips a version already installed unless --force. Fails, listing the platforms available, when the version has no bundle for yours. Warns when the version is deprecated. |
vpm uninstall <pkg>, vpm rm | Removes one version, or every version when you give none. | |
vpm list, vpm ls | --json | Lists the installed packages, versions and platforms. |
The cache is VOLT_CACHE_DIR, else $XDG_CACHE_HOME/volt (~/.cache/volt). A bundle is extracted to plugins/<publisher>/<name>/<version>/<platform>/, and the files in its bin/ and scripts/ are made executable. For workflow packages, the version's manifest is also written to manifests/<username>/<name>.json. The Python SDK's plugin hub uses the same default directory and layout. Bundles may be gzip or Zstandard tarballs; vpm detects which.
Publish
| Command | Options | What it does |
|---|---|---|
vpm init | --name <@username/name> (required), --kind <kind> (lib), --publisher <publisher>, --description <description> | Writes a vpm.json in the current directory, with version 0.1.0, license MIT and files: ["dist", "bin"]. Refuses to overwrite one. |
vpm pack [dir] | --out <file> | Writes a gzip tarball of the package: vpm.json plus the entries of files that exist, or vpm.json, README.md, LICENSE, dist and bin when files is empty. The default name is <username>-<name>-<version>.tgz. |
vpm publish [dir] | --bundle <tag>=<path> (repeatable), --bundle-dir <dir>, --token <token>, --dry-run | Uploads the version with one bundle per platform tag. |
vpm deprecate <pkg>@<version> <message> | --token <token> | Marks a published version as deprecated with a message, which vpm info and vpm install show. |
vpm publish reads vpm.json from dir (the current directory by default) and collects the bundles:
- from
--bundle <tag>=<path>options, if any; - otherwise from
--bundle-dir, orbundles/next tovpm.json: every.tgz,.tar.gzor.tar.zstfile, with the tag taken from the file name before its first dot (bundles/linux-x86_64.tar.zst).
When vpm.json lists platforms, every one of them needs a bundle; otherwise at least one bundle is required. The manifest, the package's README.md if it has one, and the bundles are uploaded in one request. --dry-run prints the version, the bundles with their sizes and whether a README was found, and uploads nothing; it still needs a token.
| Registry answer | Meaning |
|---|---|
| 201 | Published. vpm prints the latest dist-tag. |
| 401 | Bad or missing token. |
| 403 | You may not publish under that username. |
| 409 | The version already exists. Bump version. |
| 422 | The manifest is invalid. |
A typical CI publish, from the CoreToolkit workflow that builds C++ plugins:
vpm publish plugin --token "$VPM_PUBLISH_TOKEN" \
--bundle linux-x86_64=artifacts/coordination-analysis-2.1.1-linux-x86_64.tar.zst \
--bundle darwin-arm64=artifacts/coordination-analysis-2.1.1-darwin-arm64.tar.zst \
--bundle windows-x86_64=artifacts/coordination-analysis-2.1.1-windows-x86_64.tar.zst \
--bundle wasm-wasm32=artifacts/coordination-analysis-2.1.1-wasm-wasm32.tar.zstCommands on the main branch
Built from the repository's main branch, vpm also has:
| Command | Options | What it does |
|---|---|---|
vpm version [target] | -C, --dir <path> (.), --check | Sets version in vpm.json to major, minor, patch (a bump) or an explicit x.y.z, and writes the same version into the plugin.json modifier, changing only those strings. --check reports whether the two files agree, and exits with 1 when they do not. |
vpm seal <bundle-dir...> | --name <@publisher/plugin> | Runs each built bundle's program with --describe and writes the option table into plugin.json as interface.params, with a checksum. It also checks that the flags the interface block binds exist. The name comes from vpm.json when you omit --name. |
On that branch, vpm publish also refuses a bundle of a package that is not lib unless the bundle's plugin.json has an interface block (version 2) with a sealed interface.params.
Environment variables
| Variable | Default | Effect |
|---|---|---|
VPM_REGISTRY | https://registry.voltcloud.dev | Registry URL. |
VPM_CONSOLE | https://server.console.voltcloud.dev | Console URL. |
VOLT_CACHE_DIR | $XDG_CACHE_HOME/volt | Plugin cache. |
VPM_NO_KEYRING | 1 or true stores credentials in a file instead of the keychain. | |
VPM_DEBUG | 1 prints stack traces with errors. |
Registry API
vpm uses these endpoints, which other clients can use too:
| Endpoint | Returns |
|---|---|
GET /-/search?q=&kind=&page=&pageSize= | { items, page, pageSize, total, facets }. facets counts the packages of each kind. |
GET /packages/<username>/<name> | The packument: fullName, kind, description, distTags, versions (each with its manifest, SHA-256, size, publish time and publisher, and platforms), downloads, readme. |
GET /packages/<username>/<name>/<version> | One version's metadata. |
GET /packages/<username>/<name>/<version>/-/<platform>.tgz | A 307 redirect to a signed download URL. With Accept: application/json, { sha256, sizeBytes, platform } instead. 404 when the version has no bundle for that platform. |
PUT /packages/<username>/<name> | Publishes a version: a multipart form with manifest (JSON), optional readme, and one file per platform tag. Needs a bearer token. |
POST /packages/<username>/<name>/<version>/deprecate | { "reason": "…" }. Needs a bearer token. |
The VOLT server calls the search and tarball endpoints when you browse the marketplace and install a plugin. It reads the registry URL from REGISTRY_URL, https://registry.voltcloud.dev when unset (see Configuration).
Writing plugins
How a plugin's plugin.json workflow is built, how the cluster daemon runs it, the files it must write, and how to build, test and publish it.
REST & realtime API
How to call the VOLT server from your own code - base path, authentication, response envelopes, pagination and errors, the route groups, and the Socket.IO channel.