Home
go-pkgx
A dependency-free, pure-Go (CGO_ENABLED=0) package manager for
pkgx packages — a single static binary that installs and
runs packages on a FROM scratch image, with no runtime dependencies of its
own.
The reference pkgm is a Deno/TypeScript script that shells out to pkgx,
deno, curl, openssl, zlib and xz — roughly 515 MB of runtime
closure just to install a package. go-pkgx replaces all of it with one static
~9 MB binary.
⛔ Three commands, and why pkgx and pkgs are two of them
pkgm | installs a package, into a store you keep |
pkgx | runs one, on the fly, without installing it |
pkgs | answers about one: ls, search, why, catalog, store, browse, compat, env, completion |
The last two ship from one repository and share one resolver. They are separate commands because of a grammar, not a preference.
Upstream pkgx has no verbs at all: the first argument that is not a flag is
a package, and pkgx runs it. This implementation had added nine verbs in
that position, and each took a name away from the registry. Two took names
that exist — measured against the published linux/x86-64 catalogue on
2026-10-18, gnu.org/coreutils provides both ls and env — so pkgx ls listed a catalogue where upstream runs GNU ls, and there was no way to
ask for the other one.
$ pkgx ls --version # GNU coreutils, as upstream does
ls (GNU coreutils) 9.11
$ pkgs ls gnu.org # what is available under a node
$ pkgx -Q ls # which project provides a command
gnu.org/coreutils
A reserved word could not have fixed it: vercel.com/pkg provides the
command pkg, and any bare word can become a package tomorrow, because the
point of the first position is that the registry decides what is in it.
Only a token the grammar already reserves is safe — a flag, which is what
upstream chose for pkgx -Q, or a different binary.
Install
Linux / macOS — one line, naming the release you want:
curl -fsSL https://go-pkgx.github.io/install.sh | sh -s -- pkgm v0.5.1Windows (PowerShell) — irm | iex passes no arguments, so the version goes
in the environment:
$env:PKGM_VERSION='v0.5.1'; irm https://go-pkgx.github.io/install.ps1 | iexThe installer downloads the static binary for your os/arch from that
release, verifies it against the
release SHA256SUMS, and puts pkgm on your PATH ($HOME/.local/bin, or
%LOCALAPPDATA%\Programs\go-pkgx on Windows; set PKGM_INSTALL to override on
Unix).
⛔ SHA256SUMS is a checksum of itself. It says the binaries match a
manifest published beside them, and whoever replaced the binaries would
replace the manifest. The half that cannot be forged from the release page is
GitHub’s provenance attestation — signed through Sigstore, saying that the
repository’s own release workflow built that file from that commit:
gh attestation verify -R go-pkgx/pkgm pkgm-linux-amd64Every file of every pkgx, pkgs, bk, pkgm and mirror release
published from 2026-10-20 carries one. Earlier releases carry none, and
gh attestation verify answers HTTP 404 for those rather than failing
closed, so a 404 on an older binary means unattested, not tampered with.
The version is named on purpose: this line copied today and the same line
copied in six months install the same bytes, and a bad release does not reach
everyone who happens to install that hour. To track releases instead, say so —
sh -s -- pkgm latest, or PKGM_VERSION=latest, which is also what a bare
| sh has always done. Re-running is the updater; it skips the download when
the target version is already installed.
To install pkgx (the runtime) or mirror instead, name it:
sh -s -- pkgx v0.27.1, sh -s -- mirror v0.3.12.
Go users:
go install github.com/go-pkgx/pkgm@latestOnce installed, pkgm install lz4.org verifies each bottle against the signed
registry by default.
Usage
pkgm install|i <pkg>[@version] ... install to /usr/local (root) or ~/.local
pkgm uninstall|rm <pkg> ... remove an installation
pkgm shim|stub <pkg> ... create a shim in <prefix>/bin
pkgm list|ls list what's installed
pkgm outdated list outdated installations
pkgm update|up|upgrade update installations to latest
pkgm pin <pkg>@version ... install pinned to an exact version
pkgm run|x <pkg> [-- args...] run a pkg (works FROM scratch)
flags: -h/--help -v/--version -p/--pin -P/--prefix DIR -s/--from-scratch
env: PKGX_DIR package store (default: ~/.pkgx)
PKGX_DIST package source (default: oci://ghcr.io/go-pkgx/packages, signed)
PKGX_VERIFY verify signatures, fail-closed (default: on)
PKGM_PREFIX default install prefix (ideal for FROM scratch)The install/uninstall/shim/list/outdated/update/pin command
surface and the ~/.local vs /usr/local prefix logic mirror the reference
pkgm, so it is a drop-in replacement.
FROM scratch
A scratch image whose only file is the pkgm binary can install and run real
packages, with no system libc:
FROM scratch
COPY pkgm /pkgm
ENV PKGX_DIR=/pkgx
ENTRYPOINT ["/pkgm"]$ docker run --rm pkgm-scratch run gnu.org/bash -- --version
GNU bash, version 5.3.0(1)-release (aarch64-unknown-linux-gnu)See FROM scratch for how the closure is resolved, the conformance matrix, and the pantry-wide audit.
Environments
pkgx also carries pkge, a module system: named environments declared in
HCL2, load/unload/purge that are exact rather than incremental, and a
converter that reads a site’s existing Lmod and Environment Modules files.
$ eval "$(pkgs env init)"
$ pkge load cfd
See Environments and HPC.