unpm
Vendor static JS, CSS, and SVG assets directly into your repository. No node_modules, no runtime CDN dependency, no build step.
unpm fetches versioned files from npm and GitHub packages via jsdelivr, locks them with SHA-256 hashes, and checks for known vulnerabilities.
Install
Download a prebuilt binary from releases, or build from source:
cargo install --path .
Quick Start
# Add a package (interactive)
# Add with a specific version
# Add a GitHub-hosted package
# Fetch all vendored files
# Verify integrity and scan for CVEs
Files are vendored into static/vendor/ by default. Configure in .unpm.toml:
= "assets/vendor"
= true # remove untracked files from output dir (default)
Manifest
Dependencies are declared in unpm.toml:
[]
= "2.0.7"
= { = "0.7.2", = "dist/idiomorph.js" }
= { = "1.0.12", = "fasthtml.js" }
= { = "1.6.31", = ["dist/uPlot.min.js", "dist/uPlot.min.css"] }
Short form uses the package's default entry point. Extended form allows specifying a file path within the package, or files for multiple files. You can also set a custom url (single file only) or a list of CVEs to ignore:
[]
= { = "4.17.21", = "lodash.min.js", = ["GHSA-x5rq-j2xg-h7qm"] }
file and files are mutually exclusive.
Commands
| Command | Description |
|---|---|
unpm add <package[@version]> |
Add a dependency (interactive) |
unpm install |
Fetch all dependencies |
unpm check |
Verify integrity, CVEs, and freshness |
unpm list |
List all dependencies and their files |
unpm outdated |
Show dependencies with newer versions available |
unpm update [package[@version]] |
Update one or all dependencies (same major) |
unpm remove <package> |
Remove a dependency |
unpm add
Interactive flow: select version, pick files from the package (multi-select), confirm.
Non-interactive mode for CI:
# Add multiple files from the same package
Running add on an existing package appends the new files to it.
unpm check
Runs four checks per dependency (integrity checks run per file):
- Lockfile SHA -- vendored file matches the hash recorded at add time
- CDN SHA -- vendored file matches the hash jsdelivr currently reports (independent second source)
- CVE scan -- no known vulnerabilities via OSV.dev
- Freshness -- whether newer versions are available
Output is grouped by category (Integrity, Vulnerabilities, Outdated). Only problems are printed.
unpm update
Updates dependencies within the same major version by default. For multi-file dependencies, all files are updated atomically -- if any file fails to fetch at the new version, none are updated.
If a newer major version exists but can't be installed, unpm tells you:
htmx.org: 1.9.12 held back (2.0.7 available, use --latest to update across major versions)
Versioning
The version in unpm.toml is the exact version that gets installed — there are no version ranges or specifiers. When you run unpm update, it upgrades to the latest version within the same major version (e.g. 1.9.12 → 1.9.14, but not 1.9.12 → 2.0.0). This keeps updates safe by default, since major version bumps typically indicate breaking changes.
To update across major versions, use unpm update --latest or specify the version explicitly with unpm update package@version.
Package Sources
unpm supports two package sources, both resolved via jsdelivr:
- npm --
unpm add htmx.org - GitHub --
unpm add gh:user/repo
Package names must be exact. No fuzzy matching or alias resolution -- this prevents typosquatting attacks.
Security
- SHA-256 integrity -- every vendored file is hashed and locked. Tampering is detected on
checkandinstall. - CDN cross-checking --
checkverifies vendored files against jsdelivr's independently computed hashes, not just the hash from first download. - CVE scanning -- queries the OSV.dev vulnerability database for each package/version.
- No post-install scripts -- unpm vendors static files only. No code execution, no transitive dependencies.
- Exact package names -- no fuzzy resolution.
unpm add htmxfails;unpm add htmx.orgsucceeds. - Reviewable diffs -- both
unpm.tomlandunpm.lockare committed to the repo, making dependency changes visible in PRs.
GitHub Action
Add to your CI workflow to verify vendored dependencies on every push:
- uses: JamesGuthrie/unpm@v1
The action downloads the unpm binary and runs unpm check. It exits non-zero on SHA mismatches or known vulnerabilities.
Inputs
| Input | Description | Default |
|---|---|---|
allow-vulnerable |
Allow known vulnerabilities | false |
version |
unpm version to use | latest |
Example workflow
name: Verify vendored deps
on:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: JamesGuthrie/unpm@v1